From 208caa8be0be75a0a297e4e7787f96ce59b82083 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 16 Jul 2015 10:26:36 -0700 Subject: [PATCH] Update to mojo 5f5dfcc9af8b40a14dd76e34c60a5766b9f58bb4 --- engine/src/flutter/build/OWNERS | 5 + engine/src/flutter/build/PRESUBMIT.py | 16 + engine/src/flutter/build/README.chromium | 15 + engine/src/flutter/build/all.gyp | 1409 +++++ .../flutter/build/android/AndroidManifest.xml | 20 + .../build/android/CheckInstallApk-debug.apk | Bin 0 -> 37106 bytes engine/src/flutter/build/android/OWNERS | 3 + engine/src/flutter/build/android/PRESUBMIT.py | 64 + .../android/adb_android_webview_command_line | 20 + .../android/adb_chrome_public_command_line | 19 + .../android/adb_chrome_shell_command_line | 20 + .../android/adb_command_line_functions.sh | 40 + .../android/adb_content_shell_command_line | 20 + .../build/android/adb_device_functions.sh | 139 + engine/src/flutter/build/android/adb_gdb | 1047 ++++ .../android/adb_gdb_android_webview_shell | 16 + .../build/android/adb_gdb_chrome_public | 16 + .../build/android/adb_gdb_chrome_shell | 16 + .../build/android/adb_gdb_content_shell | 16 + .../build/android/adb_gdb_cronet_sample | 16 + .../flutter/build/android/adb_gdb_mojo_shell | 16 + .../flutter/build/android/adb_install_apk.py | 89 + .../android/adb_kill_android_webview_shell | 24 + .../build/android/adb_kill_chrome_public | 24 + .../build/android/adb_kill_chrome_shell | 24 + .../build/android/adb_kill_content_shell | 24 + .../build/android/adb_logcat_monitor.py | 156 + .../build/android/adb_logcat_printer.py | 213 + .../flutter/build/android/adb_profile_chrome | 8 + .../build/android/adb_reverse_forwarder.py | 80 + .../android/adb_run_android_webview_shell | 12 + .../build/android/adb_run_chrome_public | 12 + .../build/android/adb_run_chrome_shell | 12 + .../build/android/adb_run_content_shell | 12 + .../flutter/build/android/adb_run_mojo_shell | 16 + .../build/android/android_no_jni_exports.lst | 17 + .../flutter/build/android/ant/apk-package.xml | 96 + .../build/android/ant/chromium-debug.keystore | Bin 0 -> 2223 bytes .../flutter/build/android/ant/empty/res/.keep | 2 + .../flutter/build/android/asan_symbolize.py | 103 + engine/src/flutter/build/android/avd.py | 96 + .../build/android/bb_run_sharded_steps.py | 41 + .../src/flutter/build/android/buildbot/OWNERS | 6 + .../build/android/buildbot/bb_annotations.py | 46 + .../buildbot/bb_device_status_check.py | 404 ++ .../build/android/buildbot/bb_device_steps.py | 796 +++ .../build/android/buildbot/bb_host_steps.py | 133 + .../build/android/buildbot/bb_run_bot.py | 320 + .../build/android/buildbot/bb_utils.py | 100 + .../build/android/buildbot/env_to_json.py | 11 + .../android/buildbot/tests/bb_run_bot_test.py | 35 + .../build/android/chrome_with_libs.gyp | 82 + .../src/flutter/build/android/empty/src/.keep | 6 + .../build/android/empty_proguard.flags | 1 + .../flutter/build/android/enable_asserts.py | 42 + engine/src/flutter/build/android/envsetup.sh | 62 + .../flutter/build/android/findbugs_diff.py | 110 + .../findbugs_filter/findbugs_exclude.xml | 23 + .../build/android/generate_emma_html.py | 90 + engine/src/flutter/build/android/gn/zip.py | 49 + engine/src/flutter/build/android/gyp/aidl.py | 54 + engine/src/flutter/build/android/gyp/ant.py | 65 + .../flutter/build/android/gyp/apk_install.py | 164 + .../build/android/gyp/apk_obfuscate.py | 147 + .../src/flutter/build/android/gyp/copy_ex.py | 77 + .../gyp/create_device_library_links.py | 114 + .../build/android/gyp/create_dist_jar.py | 36 + .../android/gyp/create_java_binary_script.py | 76 + .../android/gyp/create_placeholder_files.py | 35 + .../android/gyp/create_standalone_apk.py | 60 + .../android/gyp/create_test_runner_script.py | 92 + engine/src/flutter/build/android/gyp/dex.py | 82 + .../flutter/build/android/gyp/emma_instr.py | 207 + .../flutter/build/android/gyp/finalize_apk.py | 132 + .../build/android/gyp/finalize_splits.py | 44 + engine/src/flutter/build/android/gyp/find.py | 30 + .../build/android/gyp/gcc_preprocess.py | 58 + .../android/gyp/generate_split_manifest.py | 97 + .../gyp/generate_v14_compatible_resources.py | 319 + .../android/gyp/get_device_configuration.py | 67 + .../android/gyp/insert_chromium_version.py | 66 + engine/src/flutter/build/android/gyp/jar.py | 79 + .../src/flutter/build/android/gyp/jar_toc.py | 127 + .../build/android/gyp/java_cpp_enum.py | 340 ++ .../build/android/gyp/java_cpp_enum_tests.py | 436 ++ engine/src/flutter/build/android/gyp/javac.py | 282 + .../build/android/gyp/jinja_template.py | 121 + engine/src/flutter/build/android/gyp/lint.py | 215 + .../build/android/gyp/pack_arm_relocations.py | 107 + .../build/android/gyp/package_resources.py | 245 + .../build/android/gyp/process_resources.py | 420 ++ .../src/flutter/build/android/gyp/proguard.py | 69 + .../build/android/gyp/push_libraries.py | 80 + .../android/gyp/strip_library_for_device.py | 61 + .../flutter/build/android/gyp/test/BUILD.gn | 13 + .../chromium/helloworld/HelloWorldMain.java | 15 + .../helloworld/HelloWorldPrinter.java | 12 + engine/src/flutter/build/android/gyp/touch.py | 16 + .../build/android/gyp/util/__init__.py | 4 + .../build/android/gyp/util/build_device.py | 105 + .../build/android/gyp/util/build_utils.py | 376 ++ .../build/android/gyp/util/md5_check.py | 86 + .../build/android/gyp/util/md5_check_test.py | 72 + .../build/android/gyp/util/proguard_util.py | 128 + .../build/android/gyp/write_build_config.py | 356 ++ .../android/gyp/write_ordered_libraries.py | 139 + engine/src/flutter/build/android/gyp/zip.py | 26 + .../flutter/build/android/host_heartbeat.py | 33 + .../build/android/install_emulator_deps.py | 277 + .../flutter/build/android/lighttpd_server.py | 256 + engine/src/flutter/build/android/lint/OWNERS | 2 + .../flutter/build/android/lint/suppress.py | 115 + .../build/android/lint/suppressions.xml | 100 + engine/src/flutter/build/android/ndk.gyp | 20 + .../build/android/provision_devices.py | 329 ++ engine/src/flutter/build/android/pylib/OWNERS | 4 + .../flutter/build/android/pylib/__init__.py | 3 + .../build/android/pylib/android_commands.py | 1976 +++++++ .../pylib/android_commands_unittest.py | 191 + .../build/android/pylib/base/__init__.py | 4 + .../build/android/pylib/base/base_setup.py | 63 + .../android/pylib/base/base_test_result.py | 216 + .../pylib/base/base_test_result_unittest.py | 82 + .../android/pylib/base/base_test_runner.py | 138 + .../build/android/pylib/base/environment.py | 34 + .../android/pylib/base/environment_factory.py | 18 + .../android/pylib/base/test_collection.py | 80 + .../android/pylib/base/test_dispatcher.py | 332 ++ .../pylib/base/test_dispatcher_unittest.py | 241 + .../build/android/pylib/base/test_instance.py | 35 + .../pylib/base/test_instance_factory.py | 24 + .../build/android/pylib/base/test_run.py | 39 + .../android/pylib/base/test_run_factory.py | 41 + .../build/android/pylib/base/test_server.py | 19 + .../pylib/chrome_test_server_spawner.py | 422 ++ .../flutter/build/android/pylib/cmd_helper.py | 261 + .../build/android/pylib/cmd_helper_test.py | 83 + .../build/android/pylib/constants/__init__.py | 308 + .../build/android/pylib/constants/keyevent.py | 14 + .../build/android/pylib/content_settings.py | 82 + .../flutter/build/android/pylib/device/OWNERS | 2 + .../build/android/pylib/device/__init__.py | 0 .../build/android/pylib/device/adb_wrapper.py | 608 ++ .../android/pylib/device/adb_wrapper_test.py | 96 + .../android/pylib/device/battery_utils.py | 595 ++ .../pylib/device/battery_utils_test.py | 574 ++ .../android/pylib/device/commands/BUILD.gn | 17 + .../android/pylib/device/commands/__init__.py | 3 + .../pylib/device/commands/commands.gyp | 20 + .../pylib/device/commands/install_commands.py | 51 + .../android/commands/unzip/Unzip.java | 94 + .../build/android/pylib/device/decorators.py | 157 + .../android/pylib/device/decorators_test.py | 365 ++ .../android/pylib/device/device_blacklist.py | 61 + .../android/pylib/device/device_errors.py | 89 + .../build/android/pylib/device/device_list.py | 30 + .../android/pylib/device/device_utils.py | 1746 ++++++ .../pylib/device/device_utils_device_test.py | 211 + .../android/pylib/device/device_utils_test.py | 1845 ++++++ .../build/android/pylib/device/intent.py | 113 + .../android/pylib/device/logcat_monitor.py | 139 + .../pylib/device/logcat_monitor_test.py | 164 + .../android/pylib/device/shared_prefs.py | 391 ++ .../android/pylib/device/shared_prefs_test.py | 169 + .../build/android/pylib/device_settings.py | 186 + .../build/android/pylib/device_signal.py | 41 + .../pylib/efficient_android_directory_copy.sh | 78 + .../build/android/pylib/flag_changer.py | 166 + .../flutter/build/android/pylib/forwarder.py | 331 ++ .../build/android/pylib/gtest/__init__.py | 4 + .../build/android/pylib/gtest/filter/OWNERS | 1 + .../gtest/filter/base_unittests_disabled | 28 + ...ase_unittests_emulator_additional_disabled | 10 + .../filter/blink_heap_unittests_disabled | 2 + .../gtest/filter/breakpad_unittests_disabled | 9 + .../pylib/gtest/filter/cc_unittests_disabled | 5 + .../filter/content_browsertests_disabled | 65 + .../gtest/filter/content_unittests_disabled | 13 + .../pylib/gtest/filter/gfx_unittests_disabled | 10 + .../pylib/gtest/filter/ipc_tests_disabled | 18 + .../gtest/filter/media_unittests_disabled | 8 + .../pylib/gtest/filter/net_unittests_disabled | 41 + .../gtest/filter/sync_unit_tests_disabled | 4 + .../pylib/gtest/filter/unit_tests_disabled | 119 + .../gtest/filter/webkit_unit_tests_disabled | 25 + .../build/android/pylib/gtest/gtest_config.py | 54 + .../pylib/gtest/gtest_test_instance.py | 329 ++ .../pylib/gtest/gtest_test_instance_test.py | 86 + .../pylib/gtest/local_device_gtest_run.py | 241 + .../build/android/pylib/gtest/setup.py | 230 + .../build/android/pylib/gtest/test_options.py | 19 + .../build/android/pylib/gtest/test_package.py | 76 + .../android/pylib/gtest/test_package_apk.py | 151 + .../android/pylib/gtest/test_package_exe.py | 163 + .../build/android/pylib/gtest/test_runner.py | 217 + .../android/pylib/host_driven/__init__.py | 4 + .../build/android/pylib/host_driven/setup.py | 200 + .../android/pylib/host_driven/test_case.py | 189 + .../pylib/host_driven/test_info_collection.py | 144 + .../android/pylib/host_driven/test_runner.py | 133 + .../android/pylib/host_driven/test_server.py | 130 + .../pylib/host_driven/tests_annotations.py | 94 + .../android/pylib/instrumentation/__init__.py | 4 + .../instrumentation/instrumentation_parser.py | 96 + .../instrumentation_parser_test.py | 134 + .../instrumentation_test_instance.py | 525 ++ .../instrumentation_test_instance_test.py | 109 + .../pylib/instrumentation/json_perf_parser.py | 161 + .../android/pylib/instrumentation/setup.py | 113 + .../android/pylib/instrumentation/test_jar.py | 230 + .../pylib/instrumentation/test_options.py | 27 + .../pylib/instrumentation/test_package.py | 42 + .../pylib/instrumentation/test_result.py | 30 + .../pylib/instrumentation/test_runner.py | 374 ++ .../build/android/pylib/junit/__init__.py | 4 + .../build/android/pylib/junit/setup.py | 20 + .../android/pylib/junit/test_dispatcher.py | 28 + .../build/android/pylib/junit/test_runner.py | 50 + .../build/android/pylib/linker/__init__.py | 4 + .../build/android/pylib/linker/setup.py | 45 + .../build/android/pylib/linker/test_case.py | 496 ++ .../build/android/pylib/linker/test_runner.py | 98 + .../build/android/pylib/local/__init__.py | 3 + .../android/pylib/local/device/__init__.py | 3 + .../local/device/local_device_environment.py | 54 + .../local_device_instrumentation_test_run.py | 207 + .../local/device/local_device_test_run.py | 99 + .../pylib/local/local_test_server_spawner.py | 45 + .../build/android/pylib/monkey/__init__.py | 0 .../build/android/pylib/monkey/setup.py | 27 + .../android/pylib/monkey/test_options.py | 16 + .../build/android/pylib/monkey/test_runner.py | 106 + .../build/android/pylib/perf/__init__.py | 3 + .../build/android/pylib/perf/cache_control.py | 21 + .../build/android/pylib/perf/perf_control.py | 161 + .../pylib/perf/perf_control_unittest.py | 37 + .../flutter/build/android/pylib/perf/setup.py | 97 + .../pylib/perf/surface_stats_collector.py | 191 + .../perf/surface_stats_collector_unittest.py | 64 + .../build/android/pylib/perf/test_options.py | 21 + .../build/android/pylib/perf/test_runner.py | 368 ++ .../android/pylib/perf/thermal_throttle.py | 137 + .../flutter/build/android/pylib/pexpect.py | 21 + .../src/flutter/build/android/pylib/ports.py | 172 + .../build/android/pylib/remote/__init__.py | 3 + .../android/pylib/remote/device/__init__.py | 3 + .../pylib/remote/device/appurify_constants.py | 57 + .../pylib/remote/device/appurify_sanitized.py | 40 + .../pylib/remote/device/dummy/BUILD.gn | 14 + .../pylib/remote/device/dummy/dummy.gyp | 25 + .../dummy/src/org/chromium/dummy/Dummy.java | 9 + .../device/remote_device_environment.py | 368 ++ .../remote/device/remote_device_gtest_run.py | 81 + .../remote/device/remote_device_helper.py | 24 + .../remote_device_instrumentation_test_run.py | 74 + .../remote/device/remote_device_test_run.py | 308 + .../device/remote_device_uirobot_test_run.py | 88 + .../build/android/pylib/restart_adbd.sh | 20 + .../build/android/pylib/results/__init__.py | 3 + .../results/flakiness_dashboard/__init__.py | 3 + .../json_results_generator.py | 697 +++ .../flakiness_dashboard/results_uploader.py | 181 + .../android/pylib/results/json_results.py | 139 + .../pylib/results/json_results_test.py | 133 + .../android/pylib/results/report_results.py | 114 + .../flutter/build/android/pylib/screenshot.py | 99 + .../build/android/pylib/sdk/__init__.py | 3 + .../build/android/pylib/sdk/dexdump.py | 30 + .../build/android/pylib/sdk/split_select.py | 66 + .../build/android/pylib/symbols/PRESUBMIT.py | 21 + .../build/android/pylib/symbols/__init__.py | 0 .../android/pylib/symbols/elf_symbolizer.py | 467 ++ .../pylib/symbols/elf_symbolizer_unittest.py | 173 + .../pylib/symbols/mock_addr2line/__init__.py | 0 .../symbols/mock_addr2line/mock_addr2line | 79 + .../build/android/pylib/system_properties.py | 40 + .../android/pylib/uiautomator/__init__.py | 4 + .../build/android/pylib/uiautomator/setup.py | 35 + .../android/pylib/uiautomator/test_options.py | 20 + .../android/pylib/uiautomator/test_package.py | 33 + .../android/pylib/uiautomator/test_runner.py | 89 + .../build/android/pylib/uirobot/__init__.py | 4 + .../pylib/uirobot/uirobot_test_instance.py | 79 + .../build/android/pylib/utils/__init__.py | 0 .../build/android/pylib/utils/apk_helper.py | 117 + .../build/android/pylib/utils/base_error.py | 16 + .../pylib/utils/command_option_parser.py | 75 + .../android/pylib/utils/device_temp_file.py | 57 + .../pylib/utils/device_temp_file_test.py | 91 + .../build/android/pylib/utils/emulator.py | 444 ++ .../build/android/pylib/utils/findbugs.py | 154 + .../android/pylib/utils/host_path_finder.py | 22 + .../build/android/pylib/utils/host_utils.py | 16 + .../build/android/pylib/utils/isolator.py | 173 + .../utils/json_results_generator_unittest.py | 213 + .../android/pylib/utils/logging_utils.py | 27 + .../build/android/pylib/utils/md5sum.py | 91 + .../build/android/pylib/utils/md5sum_test.py | 231 + .../build/android/pylib/utils/mock_calls.py | 182 + .../android/pylib/utils/mock_calls_test.py | 175 + .../build/android/pylib/utils/parallelizer.py | 242 + .../android/pylib/utils/parallelizer_test.py | 166 + .../build/android/pylib/utils/proguard.py | 148 + .../build/android/pylib/utils/repo_utils.py | 16 + .../android/pylib/utils/reraiser_thread.py | 158 + .../pylib/utils/reraiser_thread_unittest.py | 96 + .../android/pylib/utils/run_tests_helper.py | 44 + .../android/pylib/utils/test_environment.py | 47 + .../build/android/pylib/utils/time_profile.py | 26 + .../android/pylib/utils/timeout_retry.py | 167 + .../pylib/utils/timeout_retry_unittest.py | 52 + .../android/pylib/utils/watchdog_timer.py | 47 + .../flutter/build/android/pylib/utils/xvfb.py | 58 + .../build/android/pylib/utils/zip_utils.py | 31 + .../build/android/pylib/valgrind_tools.py | 304 + engine/src/flutter/build/android/rezip.gyp | 45 + .../src/flutter/build/android/rezip/BUILD.gn | 11 + .../flutter/build/android/rezip/RezipApk.java | 448 ++ .../src/flutter/build/android/screenshot.py | 98 + engine/src/flutter/build/android/setup.gyp | 82 + engine/src/flutter/build/android/symbolize.py | 88 + .../flutter/build/android/symbolize_test.py | 130 + .../src/flutter/build/android/test_runner.py | 1061 ++++ .../multiple_proguards/AndroidManifest.xml | 30 + .../multiple_proguards/multiple_proguards.gyp | 34 + .../tests/multiple_proguards/proguard1.flags | 1 + .../tests/multiple_proguards/proguard2.flags | 1 + .../src/dummy/DummyActivity.java | 26 + .../src/dummy/NativeLibraries.java | 17 + .../build/android/tests/symbolize/Makefile | 11 + .../build/android/tests/symbolize/a.cc | 14 + .../build/android/tests/symbolize/b.cc | 14 + .../src/flutter/build/android/tombstones.py | 252 + .../build/android/update_verification.py | 108 + .../src/flutter/build/android_sdk_extras.json | 8 + engine/src/flutter/build/apply_locales.py | 45 + engine/src/flutter/build/branding_value.sh | 51 + engine/src/flutter/build/build-ctags.sh | 49 + engine/src/flutter/build/build_config.h | 168 + .../src/flutter/build/check_return_value.py | 17 + engine/src/flutter/build/common.croc | 127 + engine/src/flutter/build/compiled_action.gni | 173 + engine/src/flutter/build/compiler_version.py | 143 + engine/src/flutter/build/config/BUILD.gn | 398 ++ .../src/flutter/build/config/BUILDCONFIG.gn | 777 +++ engine/src/flutter/build/config/OWNERS | 6 + engine/src/flutter/build/config/allocator.gni | 16 + .../src/flutter/build/config/android/BUILD.gn | 32 + .../src/flutter/build/config/android/OWNERS | 1 + .../flutter/build/config/android/config.gni | 192 + .../build/config/android/internal_rules.gni | 1511 +++++ .../flutter/build/config/android/rules.gni | 2121 +++++++ engine/src/flutter/build/config/arm.gni | 72 + .../src/flutter/build/config/chrome_build.gni | 22 + .../src/flutter/build/config/clang/BUILD.gn | 62 + .../src/flutter/build/config/clang/clang.gni | 9 + .../flutter/build/config/compiler/BUILD.gn | 1262 ++++ engine/src/flutter/build/config/crypto.gni | 30 + engine/src/flutter/build/config/features.gni | 199 + engine/src/flutter/build/config/gcc/BUILD.gn | 44 + .../flutter/build/config/gcc/gcc_version.gni | 26 + engine/src/flutter/build/config/ios/BUILD.gn | 22 + .../src/flutter/build/config/ios/ios_sdk.gni | 30 + .../src/flutter/build/config/ios/ios_sdk.py | 19 + .../src/flutter/build/config/linux/BUILD.gn | 252 + .../flutter/build/config/linux/gtk/BUILD.gn | 45 + .../flutter/build/config/linux/pkg-config.py | 200 + .../flutter/build/config/linux/pkg_config.gni | 83 + .../build/config/linux/sysroot_ld_path.py | 20 + engine/src/flutter/build/config/locales.gni | 118 + engine/src/flutter/build/config/mac/BUILD.gn | 36 + .../src/flutter/build/config/mac/mac_sdk.gni | 41 + engine/src/flutter/build/config/mips.gni | 43 + .../flutter/build/config/sanitizers/BUILD.gn | 62 + .../build/config/sanitizers/sanitizers.gni | 22 + engine/src/flutter/build/config/sysroot.gni | 62 + engine/src/flutter/build/config/ui.gni | 67 + engine/src/flutter/build/config/win/BUILD.gn | 181 + .../config/win/visual_studio_version.gni | 39 + .../src/flutter/build/copy_test_data_ios.py | 105 + engine/src/flutter/build/cp.py | 23 + engine/src/flutter/build/detect_host_arch.py | 40 + engine/src/flutter/build/dir_exists.py | 23 + .../src/flutter/build/download_gold_plugin.py | 45 + .../flutter/build/download_nacl_toolchains.py | 59 + .../src/flutter/build/download_sdk_extras.py | 71 + engine/src/flutter/build/env_dump.py | 56 + engine/src/flutter/build/extract_from_cab.py | 63 + .../src/flutter/build/find_isolated_tests.py | 78 + engine/src/flutter/build/gdb-add-index | 162 + engine/src/flutter/build/get_landmines.py | 84 + .../flutter/build/get_sdk_extras_packages.py | 21 + .../src/flutter/build/get_syzygy_binaries.py | 451 ++ engine/src/flutter/build/git-hooks/OWNERS | 3 + engine/src/flutter/build/git-hooks/pre-commit | 60 + engine/src/flutter/build/gn_helpers.py | 39 + engine/src/flutter/build/gn_run_binary.py | 22 + engine/src/flutter/build/gyp_chromium | 333 ++ engine/src/flutter/build/gyp_chromium.py | 18 + .../flutter/build/gyp_chromium_test.py.remove | 66 + engine/src/flutter/build/gyp_environment.py | 33 + engine/src/flutter/build/gyp_helper.py | 68 + engine/src/flutter/build/gypi_to_gn.py | 167 + .../src/flutter/build/install-android-sdks.sh | 25 + .../build/install-build-deps-android.sh | 100 + .../flutter/build/install-build-deps-mac.sh | 64 + .../src/flutter/build/install-build-deps.sh | 462 ++ engine/src/flutter/build/install-chroot.sh | 858 +++ .../flutter/build/internal/README.chromium | 24 + engine/src/flutter/build/inverse_depth.py | 24 + engine/src/flutter/build/ios/OWNERS | 4 + engine/src/flutter/build/ios/PRESUBMIT.py | 42 + engine/src/flutter/build/ios/chrome_ios.croc | 71 + engine/src/flutter/build/ios/clean_env.py | 77 + .../src/flutter/build/ios/grit_whitelist.txt | 1158 ++++ engine/src/flutter/build/json_schema_api.gni | 242 + engine/src/flutter/build/landmine_utils.py | 120 + engine/src/flutter/build/landmines.py | 214 + engine/src/flutter/build/ls.py | 31 + engine/src/flutter/build/module_args/mojo.gni | 16 + engine/src/flutter/build/module_args/v8.gni | 13 + .../src/flutter/build/output_dll_copy.rules | 17 + engine/src/flutter/build/precompile.cc | 7 + engine/src/flutter/build/precompile.h | 109 + engine/src/flutter/build/protoc_java.py | 68 + engine/src/flutter/build/rmdir_and_stamp.py | 45 + .../flutter/build/sanitize-mac-build-log.sed | 33 + .../flutter/build/sanitize-mac-build-log.sh | 5 + .../flutter/build/sanitize-win-build-log.sed | 15 + .../flutter/build/sanitize-win-build-log.sh | 5 + engine/src/flutter/build/sanitizers/BUILD.gn | 24 + engine/src/flutter/build/sanitizers/OWNERS | 4 + .../build/sanitizers/asan_suppressions.cc | 23 + .../build/sanitizers/lsan_suppressions.cc | 105 + .../build/sanitizers/sanitizer_options.cc | 164 + .../flutter/build/sanitizers/sanitizers.gyp | 92 + .../build/sanitizers/tsan_suppressions.cc | 319 + .../build/secondary/testing/gmock/BUILD.gn | 54 + .../build/secondary/testing/gtest/BUILD.gn | 125 + .../third_party/android_tools/BUILD.gn | 94 + .../third_party/cacheinvalidation/BUILD.gn | 146 + .../src/google/cacheinvalidation/BUILD.gn | 27 + .../third_party/libjpeg_turbo/BUILD.gn | 211 + .../secondary/third_party/libsrtp/BUILD.gn | 391 ++ .../build/secondary/third_party/nss/BUILD.gn | 1211 ++++ .../build/secondary/tools/grit/BUILD.gn | 27 + .../build/secondary/tools/grit/grit_rule.gni | 474 ++ .../build/secondary/tools/grit/repack.gni | 47 + .../tools/grit/stamp_grit_sources.py | 55 + engine/src/flutter/build/slave/OWNERS | 20 + engine/src/flutter/build/slave/README | 8 + engine/src/flutter/build/some.gyp | 24 + engine/src/flutter/build/symlink.py | 44 + .../flutter/build/temp_gyp/README.chromium | 3 + .../src/flutter/build/temp_gyp/pdfsqueeze.gyp | 40 + engine/src/flutter/build/toolchain/OWNERS | 3 + .../flutter/build/toolchain/android/BUILD.gn | 131 + engine/src/flutter/build/toolchain/ccache.gni | 25 + engine/src/flutter/build/toolchain/clang.gni | 9 + .../src/flutter/build/toolchain/cros/BUILD.gn | 35 + .../flutter/build/toolchain/gcc_toolchain.gni | 230 + .../build/toolchain/get_concurrent_links.py | 64 + engine/src/flutter/build/toolchain/goma.gni | 22 + .../flutter/build/toolchain/linux/BUILD.gn | 114 + .../src/flutter/build/toolchain/mac/BUILD.gn | 234 + .../build/toolchain/mac/setup_toolchain.py | 29 + .../src/flutter/build/toolchain/nacl/BUILD.gn | 63 + .../src/flutter/build/toolchain/win/BUILD.gn | 247 + .../src/flutter/build/toolchain/win/midl.gni | 105 + .../build/toolchain/win/setup_toolchain.py | 154 + engine/src/flutter/build/tree_truth.sh | 102 + .../src/flutter/build/update-linux-sandbox.sh | 75 + engine/src/flutter/build/util/BUILD.gn | 28 + engine/src/flutter/build/util/lastchange.py | 300 + .../flutter/build/util/lib/common/__init__.py | 0 .../util/lib/common/perf_result_data_type.py | 20 + .../lib/common/perf_tests_results_helper.py | 166 + .../build/util/lib/common/unittest_util.py | 151 + .../src/flutter/build/util/lib/common/util.py | 151 + engine/src/flutter/build/util/version.py | 166 + engine/src/flutter/build/vs_toolchain.py | 259 + engine/src/flutter/build/whitespace_file.txt | 156 + .../src/flutter/build/win_is_xtree_patched.py | 26 + engine/src/flutter/mojom/BUILD.gn | 43 + engine/src/flutter/mojom/lexer.cc | 420 ++ engine/src/flutter/mojom/lexer.h | 92 + engine/src/flutter/mojom/lexer_unittest.cc | 162 + engine/src/flutter/mojom/mojom.ebnf | 63 + engine/src/flutter/skia/BUILD.gn | 658 +++ engine/src/flutter/skia/OWNERS | 20 + engine/src/flutter/skia/config/SkUserConfig.h | 307 + .../skia/config/sk_ref_cnt_ext_debug.h | 48 + .../skia/config/sk_ref_cnt_ext_release.h | 19 + engine/src/flutter/skia/skia.gyp | 150 + engine/src/flutter/skia/skia_Prefix.pch | 12 + engine/src/flutter/skia/skia_library_opts.gyp | 229 + .../flutter/skia/skia_test_expectations.txt | 51 + engine/src/flutter/skia/skia_tests.gyp | 64 + .../filter_fuzz_stub/filter_fuzz_stub.cc | 90 + engine/src/flutter/testing/BUILD.gn | 13 + engine/src/flutter/testing/OWNERS | 1 + engine/src/flutter/testing/PRESUBMIT.py | 25 + engine/src/flutter/testing/android/OWNERS | 7 + .../testing/android/appurify_support.gyp | 22 + .../testing/android/appurify_support/BUILD.gn | 15 + .../test/support/ResultsBundleGenerator.java | 30 + .../test/support/RobotiumBundleGenerator.java | 56 + .../flutter/testing/android/broker/BUILD.gn | 13 + .../broker/OnDeviceInstrumentationBroker.java | 64 + .../flutter/testing/android/driver/BUILD.gn | 21 + .../android/driver/java/AndroidManifest.xml | 23 + .../driver/OnDeviceInstrumentationDriver.java | 271 + .../flutter/testing/android/junit/BUILD.gn | 24 + .../chromium/testing/local/GtestComputer.java | 76 + .../chromium/testing/local/GtestFilter.java | 98 + .../chromium/testing/local/GtestListener.java | 80 + .../chromium/testing/local/GtestLogger.java | 109 + .../chromium/testing/local/JsonListener.java | 54 + .../chromium/testing/local/JsonLogger.java | 89 + .../testing/local/JunitTestArgParser.java | 117 + .../chromium/testing/local/JunitTestMain.java | 109 + .../local/LocalRobolectricTestRunner.java | 49 + .../chromium/testing/local/PackageFilter.java | 41 + ...obolectricClasspathDependencyResolver.java | 72 + .../chromium/testing/local/RunnerFilter.java | 45 + .../testing/local/GtestFilterTest.java | 124 + .../testing/local/GtestLoggerTest.java | 146 + .../testing/local/PackageFilterTest.java | 47 + .../testing/local/RunnerFilterTest.java | 58 + .../testing/android/junit/junit_test.gyp | 43 + .../flutter/testing/android/native_test.gyp | 71 + .../testing/android/native_test/BUILD.gn | 44 + .../android/native_test/README.chromium | 2 + .../native_test/java/AndroidManifest.xml | 39 + .../NativeBrowserTestActivity.java | 46 + .../native_test/NativeTestActivity.java | 121 + .../NativeTestInstrumentationTestRunner.java | 176 + .../native_test/NativeUnitTestActivity.java | 48 + .../native_test/native_test_jni_onload.cc | 38 + .../native_test/native_test_launcher.cc | 161 + .../native_test/native_test_launcher.h | 19 + .../android/native_test/native_test_util.cc | 50 + .../android/native_test/native_test_util.h | 39 + .../android/on_device_instrumentation.gyp | 79 + .../flutter/testing/android/reporter/BUILD.gn | 19 + .../test/reporter/TestStatusListener.java | 78 + .../test/reporter/TestStatusReceiver.java | 128 + .../test/reporter/TestStatusReporter.java | 83 + engine/src/flutter/testing/buildbot/OWNERS | 18 + .../src/flutter/testing/buildbot/PRESUBMIT.py | 24 + .../testing/buildbot/chromium.chrome.json | 1 + .../testing/buildbot/chromium.chromiumos.json | 662 +++ .../testing/buildbot/chromium.fyi.json | 3512 +++++++++++ .../testing/buildbot/chromium.linux.json | 1205 ++++ .../testing/buildbot/chromium.mac.json | 1064 ++++ .../testing/buildbot/chromium.memory.fyi.json | 499 ++ .../testing/buildbot/chromium.memory.json | 545 ++ .../testing/buildbot/chromium.perf.json | 440 ++ .../testing/buildbot/chromium.webkit.json | 1038 ++++ .../testing/buildbot/chromium.webrtc.fyi.json | 280 + .../testing/buildbot/chromium.win.json | 1476 +++++ .../testing/buildbot/chromium_arm.json | 14 + .../buildbot/chromium_memory_trybot.json | 122 + .../testing/buildbot/chromium_trybot.json | 379 ++ .../buildbot/chromium_win8_trybot.json | 19 + .../testing/buildbot/client.v8.branches.json | 1 + .../testing/buildbot/client.v8.fyi.json | 64 + engine/src/flutter/testing/buildbot/manage.py | 240 + .../buildbot/trybot_analyze_config.json | 54 + .../testing/buildbot/tryserver.blink.json | 64 + .../buildbot/tryserver.chromium.linux.json | 357 ++ .../buildbot/tryserver.chromium.mac.json | 72 + .../buildbot/tryserver.chromium.perf.json | 1 + .../buildbot/tryserver.chromium.win.json | 214 + .../testing/buildbot/tryserver.v8.json | 65 + .../browser_test_commands_linux.txt | 17 + .../chromoting/browser_tests_launcher.py | 271 + .../chromoting_integration_tests.isolate | 127 + .../testing/chromoting/integration_tests.gyp | 31 + .../example_task.isolate | 18 + .../example_test_controller.isolate | 31 + .../example_test_controller.py | 143 + .../src/flutter/testing/coverage_util_ios.cc | 15 + .../src/flutter/testing/coverage_util_ios.h | 17 + .../flutter/testing/generate_gmock_mutant.py | 450 ++ engine/src/flutter/testing/gmock.gyp | 67 + engine/src/flutter/testing/gmock_mutant.h | 5177 +++++++++++++++++ engine/src/flutter/testing/gtest.gyp | 214 + .../src/flutter/testing/gtest_ios/Default.png | Bin 0 -> 1707 bytes .../flutter/testing/gtest_ios/run-unittest.sh | 87 + .../testing/gtest_ios/unittest-Info.plist | 150 + engine/src/flutter/testing/gtest_mac.h | 48 + engine/src/flutter/testing/gtest_mac.mm | 61 + .../src/flutter/testing/gtest_mac_unittest.mm | 57 + engine/src/flutter/testing/gtest_nacl.gyp | 94 + engine/src/flutter/testing/iossim/OWNERS | 2 + engine/src/flutter/testing/iossim/iossim.gyp | 156 + engine/src/flutter/testing/iossim/iossim.mm | 1025 ++++ .../flutter/testing/iossim/redirect-stdout.sh | 25 + engine/src/flutter/testing/legion/__init__.py | 3 + .../src/flutter/testing/legion/common_lib.py | 43 + .../hello_world/controller_test.isolate | 21 + .../examples/hello_world/controller_test.py | 77 + .../examples/hello_world/task_test.isolate | 23 + .../legion/examples/hello_world/task_test.py | 27 + .../subprocess/subprocess_test.isolate | 21 + .../examples/subprocess/subprocess_test.py | 95 + .../legion/examples/subprocess/task.isolate | 22 + .../src/flutter/testing/legion/legion.isolate | 22 + .../testing/legion/legion_test_case.py | 135 + engine/src/flutter/testing/legion/process.py | 247 + .../src/flutter/testing/legion/rpc_methods.py | 51 + .../src/flutter/testing/legion/rpc_server.py | 128 + engine/src/flutter/testing/legion/run_task.py | 48 + .../flutter/testing/legion/task_controller.py | 213 + .../legion/task_registration_server.py | 55 + .../flutter/testing/legion/test_controller.py | 69 + .../flutter/testing/legion/tools/legion.py | 159 + .../flutter/testing/multiprocess_func_list.cc | 57 + .../flutter/testing/multiprocess_func_list.h | 70 + engine/src/flutter/testing/perf/BUILD.gn | 14 + engine/src/flutter/testing/perf/perf_test.cc | 204 + engine/src/flutter/testing/perf/perf_test.gyp | 18 + engine/src/flutter/testing/perf/perf_test.h | 116 + engine/src/flutter/testing/platform_test.h | 36 + .../src/flutter/testing/platform_test_mac.mm | 15 + engine/src/flutter/testing/scripts/OWNERS | 5 + .../src/flutter/testing/scripts/checkdeps.py | 46 + .../flutter/testing/scripts/checklicenses.py | 46 + .../src/flutter/testing/scripts/checkperms.py | 46 + engine/src/flutter/testing/scripts/common.py | 139 + .../testing/scripts/get_compile_targets.py | 55 + .../src/flutter/testing/scripts/gn_check.py | 57 + .../testing/scripts/gtest_perf_test.py | 81 + .../testing/scripts/gyp_flag_compare.py | 49 + .../src/flutter/testing/scripts/host_info.py | 136 + .../flutter/testing/scripts/mojo_apptest.py | 46 + .../testing/scripts/nacl_integration.py | 49 + .../scripts/telemetry_perf_unittests.py | 61 + .../testing/scripts/telemetry_unittests.py | 54 + .../flutter/testing/scripts/webkit_lint.py | 43 + .../testing/scripts/webkit_python_tests.py | 47 + .../testing/scripts/webview_licenses.py | 43 + engine/src/flutter/testing/test.gni | 256 + engine/src/flutter/testing/test_env.py | 239 + .../flutter/testing/variations/PRESUBMIT.py | 83 + .../variations/fieldtrial_testing_config.json | 1 + .../fieldtrial_testing_config_win.json | 97 + engine/src/flutter/testing/xvfb.py | 146 + .../third_party/android_testrunner/LICENSE | 202 + .../third_party/android_testrunner/OWNERS | 3 + .../android_testrunner/README.chromium | 36 + .../android_testrunner/adb_interface.py | 522 ++ .../am_instrument_parser.py | 169 + .../third_party/android_testrunner/errors.py | 46 + .../third_party/android_testrunner/logger.py | 96 + .../third_party/android_testrunner/patch.diff | 104 + .../android_testrunner/run_command.py | 192 + .../src/flutter/third_party/ashmem/BUILD.gn | 12 + engine/src/flutter/third_party/ashmem/LICENSE | 202 + engine/src/flutter/third_party/ashmem/OWNERS | 1 + .../third_party/ashmem/README.chromium | 6 + .../flutter/third_party/ashmem/ashmem-dev.c | 100 + .../src/flutter/third_party/ashmem/ashmem.gyp | 16 + .../src/flutter/third_party/ashmem/ashmem.h | 46 + .../flutter/third_party/binutils/.gitignore | 8 + .../src/flutter/third_party/binutils/LICENSE | 340 ++ .../binutils/Linux_ia32/binutils.tar.bz2.sha1 | 1 + .../binutils/Linux_x64/binutils.tar.bz2.sha1 | 1 + .../src/flutter/third_party/binutils/OWNERS | 3 + .../third_party/binutils/README.chromium | 29 + .../flutter/third_party/binutils/build-all.sh | 123 + .../flutter/third_party/binutils/build-one.sh | 20 + .../flutter/third_party/binutils/download.py | 118 + .../third_party/binutils/ehframe-race.patch | 96 + .../third_party/binutils/plugin-dso-fix.patch | 97 + .../third_party/binutils/unlock-thin.patch | 129 + .../flutter/third_party/binutils/upload.sh | 65 + .../src/flutter/third_party/dart-pkg/BUILD.gn | 75 + .../third_party/freetype-android/BUILD.gn | 50 + .../freetype-android/README.chromium | 18 + .../src/flutter/third_party/iccjpeg/BUILD.gn | 19 + .../src/flutter/third_party/iccjpeg/LICENSE | 49 + engine/src/flutter/third_party/iccjpeg/OWNERS | 1 + .../third_party/iccjpeg/README.chromium | 20 + .../src/flutter/third_party/iccjpeg/iccjpeg.c | 248 + .../flutter/third_party/iccjpeg/iccjpeg.gyp | 24 + .../src/flutter/third_party/iccjpeg/iccjpeg.h | 78 + .../src/flutter/third_party/jsr-305/BUILD.gn | 37 + .../third_party/jsr-305/README.chromium | 16 + .../flutter/third_party/jsr-305/jsr-305.gyp | 19 + engine/src/flutter/third_party/junit/BUILD.gn | 20 + engine/src/flutter/third_party/junit/LICENSE | 218 + engine/src/flutter/third_party/junit/OWNERS | 3 + .../flutter/third_party/junit/README.chromium | 10 + .../src/flutter/third_party/junit/junit.gyp | 34 + .../flutter/third_party/markupsafe/AUTHORS | 13 + .../flutter/third_party/markupsafe/LICENSE | 33 + .../markupsafe/MarkupSafe-0.18.tar.gz.md5 | 1 + .../markupsafe/MarkupSafe-0.18.tar.gz.sha512 | 1 + .../src/flutter/third_party/markupsafe/OWNERS | 3 + .../third_party/markupsafe/README.chromium | 24 + .../third_party/markupsafe/__init__.py | 234 + .../flutter/third_party/markupsafe/_compat.py | 24 + .../third_party/markupsafe/_constants.py | 267 + .../flutter/third_party/markupsafe/_native.py | 46 + .../third_party/markupsafe/_speedups.c | 239 + .../third_party/markupsafe/get_markupsafe.sh | 121 + engine/src/flutter/third_party/mesa/BUILD.gn | 696 +++ engine/src/flutter/third_party/mesa/LICENSE | 512 ++ engine/src/flutter/third_party/mesa/OWNERS | 4 + .../flutter/third_party/mesa/README.chromium | 59 + .../src/flutter/third_party/mesa/README.txt | 15 + .../flutter/third_party/mesa/chromium.patch | 2107 +++++++ .../third_party/mesa/generate_git_sha1.py | 35 + engine/src/flutter/third_party/mesa/mesa.gyp | 750 +++ .../third_party/mesa/redirectoutput.py | 29 + .../src/flutter/third_party/mockito/BUILD.gn | 28 + .../src/flutter/third_party/mockito/LICENSE | 21 + engine/src/flutter/third_party/mockito/OWNERS | 2 + .../third_party/mockito/README.chromium | 9 + .../flutter/third_party/mockito/mockito.gyp | 47 + .../src/flutter/third_party/modp_b64/BUILD.gn | 11 + engine/src/flutter/third_party/modp_b64/DEPS | 3 + .../src/flutter/third_party/modp_b64/LICENSE | 33 + .../src/flutter/third_party/modp_b64/OWNERS | 2 + .../third_party/modp_b64/README.chromium | 15 + .../flutter/third_party/modp_b64/modp_b64.cc | 265 + .../flutter/third_party/modp_b64/modp_b64.gyp | 48 + .../flutter/third_party/modp_b64/modp_b64.h | 171 + .../third_party/modp_b64/modp_b64_data.h | 482 ++ .../third_party/modp_b64/modp_b64_nacl.gyp | 26 + engine/src/flutter/third_party/ply/LICENSE | 30 + engine/src/flutter/third_party/ply/README | 271 + .../flutter/third_party/ply/README.chromium | 21 + .../src/flutter/third_party/ply/__init__.py | 36 + engine/src/flutter/third_party/ply/lex.py | 1058 ++++ .../src/flutter/third_party/ply/license.patch | 41 + engine/src/flutter/third_party/ply/yacc.py | 3276 +++++++++++ .../flutter/third_party/pymock/LICENSE.txt | 26 + engine/src/flutter/third_party/pymock/OWNERS | 1 + .../third_party/pymock/README.chromium | 10 + engine/src/flutter/third_party/pymock/mock.py | 2367 ++++++++ .../flutter/third_party/robolectric/BUILD.gn | 31 + .../flutter/third_party/robolectric/LICENSE | 177 + .../flutter/third_party/robolectric/OWNERS | 2 + .../third_party/robolectric/README.chromium | 11 + .../licenses/extreme.indiana.edu.license.txt | 47 + .../licenses/javolution.license.txt | 23 + .../licenses/pivotal.labs.license.txt | 21 + .../third_party/robolectric/robolectric.gyp | 58 + .../src/flutter/third_party/smhasher/BUILD.gn | 30 + .../src/flutter/third_party/smhasher/LICENSE | 23 + .../third_party/smhasher/README.chromium | 14 + .../flutter/third_party/smhasher/smhasher.gyp | 36 + engine/src/flutter/tools/checkperms/OWNERS | 1 + .../src/flutter/tools/checkperms/PRESUBMIT.py | 27 + .../flutter/tools/checkperms/checkperms.py | 480 ++ engine/src/flutter/tools/dart/update.py | 101 + engine/src/flutter/tools/find_depot_tools.py | 45 + engine/src/flutter/tools/gdb/gdb_chrome.py | 331 ++ .../generate_library_loader.gni | 70 + .../generate_library_loader.py | 249 + engine/src/flutter/tools/git/README | 16 + .../tools/git/for-all-touched-files.py | 126 + engine/src/flutter/tools/git/git-diff-ide.py | 93 + engine/src/flutter/tools/git/git-utils.sh | 17 + engine/src/flutter/tools/git/graph.sh | 42 + engine/src/flutter/tools/git/mass-rename.py | 50 + engine/src/flutter/tools/git/mass-rename.sh | 17 + engine/src/flutter/tools/git/mffr.py | 169 + .../flutter/tools/git/move_source_file.bat | 6 + .../src/flutter/tools/git/move_source_file.py | 235 + engine/src/flutter/tools/git/post-checkout | 22 + engine/src/flutter/tools/git/post-merge | 12 + .../flutter/tools/git/update-copyrights.sh | 7 + engine/src/flutter/tools/go/VERSION_LINUX | 1 + engine/src/flutter/tools/go/VERSION_MACOSX | 1 + engine/src/flutter/tools/go/download.py | 91 + engine/src/flutter/tools/go/upload.py | 177 + .../flutter/tools/gritsettings/resource_ids | 274 + .../flutter/tools/gyp/pylib/gyp/mac_tool.py | 610 ++ .../src/flutter/tools/idl_parser/PRESUBMIT.py | 15 + .../src/flutter/tools/idl_parser/__init__.py | 0 .../src/flutter/tools/idl_parser/idl_lexer.py | 294 + .../tools/idl_parser/idl_lexer_test.py | 99 + .../src/flutter/tools/idl_parser/idl_node.py | 217 + .../flutter/tools/idl_parser/idl_parser.py | 1285 ++++ .../tools/idl_parser/idl_parser_test.py | 106 + .../tools/idl_parser/idl_ppapi_lexer.py | 71 + .../tools/idl_parser/idl_ppapi_parser.py | 317 + .../src/flutter/tools/idl_parser/run_tests.py | 20 + engine/src/flutter/tools/licenses.py | 472 ++ engine/src/flutter/tools/linux/PRESUBMIT.py | 45 + .../tools/linux/dump-static-initializers.py | 234 + engine/src/flutter/tools/linux/procfs.py | 747 +++ .../flutter/tools/linux/tests/procfs_tests.py | 110 + .../flutter/tools/memory/asan/blacklist.txt | 5 + .../tools/protoc_wrapper/protoc_wrapper.py | 134 + .../flutter/tools/relocation_packer/BUILD.gn | 148 + .../flutter/tools/relocation_packer/LICENSE | 27 + .../tools/relocation_packer/README.TXT | 135 + .../tools/relocation_packer/config.gni | 21 + .../relocation_packer/relocation_packer.gyp | 161 + .../tools/relocation_packer/src/debug.cc | 55 + .../tools/relocation_packer/src/debug.h | 115 + .../relocation_packer/src/debug_unittest.cc | 122 + .../relocation_packer/src/delta_encoder.cc | 72 + .../relocation_packer/src/delta_encoder.h | 80 + .../src/delta_encoder_unittest.cc | 150 + .../tools/relocation_packer/src/elf_file.cc | 1317 +++++ .../tools/relocation_packer/src/elf_file.h | 134 + .../src/elf_file_unittest.cc | 164 + .../tools/relocation_packer/src/elf_traits.h | 103 + .../tools/relocation_packer/src/leb128.cc | 69 + .../tools/relocation_packer/src/leb128.h | 74 + .../relocation_packer/src/leb128_unittest.cc | 111 + .../tools/relocation_packer/src/main.cc | 175 + .../tools/relocation_packer/src/packer.cc | 124 + .../tools/relocation_packer/src/packer.h | 78 + .../relocation_packer/src/packer_unittest.cc | 250 + .../src/run_all_unittests.cc | 10 + .../src/run_length_encoder.cc | 144 + .../src/run_length_encoder.h | 81 + .../src/run_length_encoder_unittest.cc | 124 + .../tools/relocation_packer/src/sleb128.cc | 95 + .../tools/relocation_packer/src/sleb128.h | 75 + .../relocation_packer/src/sleb128_unittest.cc | 166 + .../test_data/elf_file_unittest_relocs.cc | 1014 ++++ .../generate_elf_file_unittest_relocs.py | 88 + .../generate_elf_file_unittest_relocs.sh | 35 + .../flutter/tools/remove_stale_pyc_files.py | 39 + .../tools/resources/about_credits.tmpl | 76 + .../tools/resources/about_credits_entry.tmpl | 8 + engine/src/flutter/tools/sort-headers.py | 192 + engine/src/flutter/tools/sort_sources.py | 187 + .../tools/vim/chromium.ycm_extra_conf.py | 233 + engine/src/flutter/tools/vim/clang-format.vim | 19 + engine/src/flutter/tools/vim/filetypes.vim | 9 + engine/src/flutter/tools/vim/ninja-build.vim | 115 + engine/src/flutter/tools/vim/ninja_output.py | 57 + .../src/flutter/tools/xdisplaycheck/BUILD.gn | 15 + .../tools/xdisplaycheck/xdisplaycheck.cc | 119 + .../tools/xdisplaycheck/xdisplaycheck.gyp | 19 + engine/src/flutter/tools/yes_no.py | 28 + 845 files changed, 127053 insertions(+) create mode 100644 engine/src/flutter/build/OWNERS create mode 100644 engine/src/flutter/build/PRESUBMIT.py create mode 100644 engine/src/flutter/build/README.chromium create mode 100644 engine/src/flutter/build/all.gyp create mode 100644 engine/src/flutter/build/android/AndroidManifest.xml create mode 100644 engine/src/flutter/build/android/CheckInstallApk-debug.apk create mode 100644 engine/src/flutter/build/android/OWNERS create mode 100644 engine/src/flutter/build/android/PRESUBMIT.py create mode 100755 engine/src/flutter/build/android/adb_android_webview_command_line create mode 100755 engine/src/flutter/build/android/adb_chrome_public_command_line create mode 100755 engine/src/flutter/build/android/adb_chrome_shell_command_line create mode 100755 engine/src/flutter/build/android/adb_command_line_functions.sh create mode 100755 engine/src/flutter/build/android/adb_content_shell_command_line create mode 100755 engine/src/flutter/build/android/adb_device_functions.sh create mode 100755 engine/src/flutter/build/android/adb_gdb create mode 100755 engine/src/flutter/build/android/adb_gdb_android_webview_shell create mode 100755 engine/src/flutter/build/android/adb_gdb_chrome_public create mode 100755 engine/src/flutter/build/android/adb_gdb_chrome_shell create mode 100755 engine/src/flutter/build/android/adb_gdb_content_shell create mode 100755 engine/src/flutter/build/android/adb_gdb_cronet_sample create mode 100755 engine/src/flutter/build/android/adb_gdb_mojo_shell create mode 100755 engine/src/flutter/build/android/adb_install_apk.py create mode 100755 engine/src/flutter/build/android/adb_kill_android_webview_shell create mode 100755 engine/src/flutter/build/android/adb_kill_chrome_public create mode 100755 engine/src/flutter/build/android/adb_kill_chrome_shell create mode 100755 engine/src/flutter/build/android/adb_kill_content_shell create mode 100755 engine/src/flutter/build/android/adb_logcat_monitor.py create mode 100755 engine/src/flutter/build/android/adb_logcat_printer.py create mode 100755 engine/src/flutter/build/android/adb_profile_chrome create mode 100755 engine/src/flutter/build/android/adb_reverse_forwarder.py create mode 100755 engine/src/flutter/build/android/adb_run_android_webview_shell create mode 100755 engine/src/flutter/build/android/adb_run_chrome_public create mode 100755 engine/src/flutter/build/android/adb_run_chrome_shell create mode 100755 engine/src/flutter/build/android/adb_run_content_shell create mode 100755 engine/src/flutter/build/android/adb_run_mojo_shell create mode 100644 engine/src/flutter/build/android/android_no_jni_exports.lst create mode 100644 engine/src/flutter/build/android/ant/apk-package.xml create mode 100644 engine/src/flutter/build/android/ant/chromium-debug.keystore create mode 100644 engine/src/flutter/build/android/ant/empty/res/.keep create mode 100755 engine/src/flutter/build/android/asan_symbolize.py create mode 100755 engine/src/flutter/build/android/avd.py create mode 100755 engine/src/flutter/build/android/bb_run_sharded_steps.py create mode 100644 engine/src/flutter/build/android/buildbot/OWNERS create mode 100644 engine/src/flutter/build/android/buildbot/bb_annotations.py create mode 100755 engine/src/flutter/build/android/buildbot/bb_device_status_check.py create mode 100755 engine/src/flutter/build/android/buildbot/bb_device_steps.py create mode 100755 engine/src/flutter/build/android/buildbot/bb_host_steps.py create mode 100755 engine/src/flutter/build/android/buildbot/bb_run_bot.py create mode 100644 engine/src/flutter/build/android/buildbot/bb_utils.py create mode 100755 engine/src/flutter/build/android/buildbot/env_to_json.py create mode 100755 engine/src/flutter/build/android/buildbot/tests/bb_run_bot_test.py create mode 100644 engine/src/flutter/build/android/chrome_with_libs.gyp create mode 100644 engine/src/flutter/build/android/empty/src/.keep create mode 100644 engine/src/flutter/build/android/empty_proguard.flags create mode 100755 engine/src/flutter/build/android/enable_asserts.py create mode 100755 engine/src/flutter/build/android/envsetup.sh create mode 100755 engine/src/flutter/build/android/findbugs_diff.py create mode 100644 engine/src/flutter/build/android/findbugs_filter/findbugs_exclude.xml create mode 100755 engine/src/flutter/build/android/generate_emma_html.py create mode 100755 engine/src/flutter/build/android/gn/zip.py create mode 100755 engine/src/flutter/build/android/gyp/aidl.py create mode 100755 engine/src/flutter/build/android/gyp/ant.py create mode 100755 engine/src/flutter/build/android/gyp/apk_install.py create mode 100755 engine/src/flutter/build/android/gyp/apk_obfuscate.py create mode 100755 engine/src/flutter/build/android/gyp/copy_ex.py create mode 100755 engine/src/flutter/build/android/gyp/create_device_library_links.py create mode 100755 engine/src/flutter/build/android/gyp/create_dist_jar.py create mode 100755 engine/src/flutter/build/android/gyp/create_java_binary_script.py create mode 100755 engine/src/flutter/build/android/gyp/create_placeholder_files.py create mode 100755 engine/src/flutter/build/android/gyp/create_standalone_apk.py create mode 100755 engine/src/flutter/build/android/gyp/create_test_runner_script.py create mode 100755 engine/src/flutter/build/android/gyp/dex.py create mode 100755 engine/src/flutter/build/android/gyp/emma_instr.py create mode 100755 engine/src/flutter/build/android/gyp/finalize_apk.py create mode 100755 engine/src/flutter/build/android/gyp/finalize_splits.py create mode 100755 engine/src/flutter/build/android/gyp/find.py create mode 100755 engine/src/flutter/build/android/gyp/gcc_preprocess.py create mode 100755 engine/src/flutter/build/android/gyp/generate_split_manifest.py create mode 100755 engine/src/flutter/build/android/gyp/generate_v14_compatible_resources.py create mode 100755 engine/src/flutter/build/android/gyp/get_device_configuration.py create mode 100755 engine/src/flutter/build/android/gyp/insert_chromium_version.py create mode 100755 engine/src/flutter/build/android/gyp/jar.py create mode 100755 engine/src/flutter/build/android/gyp/jar_toc.py create mode 100755 engine/src/flutter/build/android/gyp/java_cpp_enum.py create mode 100755 engine/src/flutter/build/android/gyp/java_cpp_enum_tests.py create mode 100755 engine/src/flutter/build/android/gyp/javac.py create mode 100755 engine/src/flutter/build/android/gyp/jinja_template.py create mode 100755 engine/src/flutter/build/android/gyp/lint.py create mode 100755 engine/src/flutter/build/android/gyp/pack_arm_relocations.py create mode 100755 engine/src/flutter/build/android/gyp/package_resources.py create mode 100755 engine/src/flutter/build/android/gyp/process_resources.py create mode 100755 engine/src/flutter/build/android/gyp/proguard.py create mode 100755 engine/src/flutter/build/android/gyp/push_libraries.py create mode 100755 engine/src/flutter/build/android/gyp/strip_library_for_device.py create mode 100644 engine/src/flutter/build/android/gyp/test/BUILD.gn create mode 100644 engine/src/flutter/build/android/gyp/test/java/org/chromium/helloworld/HelloWorldMain.java create mode 100644 engine/src/flutter/build/android/gyp/test/java/org/chromium/helloworld/HelloWorldPrinter.java create mode 100755 engine/src/flutter/build/android/gyp/touch.py create mode 100644 engine/src/flutter/build/android/gyp/util/__init__.py create mode 100644 engine/src/flutter/build/android/gyp/util/build_device.py create mode 100644 engine/src/flutter/build/android/gyp/util/build_utils.py create mode 100644 engine/src/flutter/build/android/gyp/util/md5_check.py create mode 100644 engine/src/flutter/build/android/gyp/util/md5_check_test.py create mode 100644 engine/src/flutter/build/android/gyp/util/proguard_util.py create mode 100755 engine/src/flutter/build/android/gyp/write_build_config.py create mode 100755 engine/src/flutter/build/android/gyp/write_ordered_libraries.py create mode 100755 engine/src/flutter/build/android/gyp/zip.py create mode 100755 engine/src/flutter/build/android/host_heartbeat.py create mode 100755 engine/src/flutter/build/android/install_emulator_deps.py create mode 100755 engine/src/flutter/build/android/lighttpd_server.py create mode 100644 engine/src/flutter/build/android/lint/OWNERS create mode 100755 engine/src/flutter/build/android/lint/suppress.py create mode 100644 engine/src/flutter/build/android/lint/suppressions.xml create mode 100644 engine/src/flutter/build/android/ndk.gyp create mode 100755 engine/src/flutter/build/android/provision_devices.py create mode 100644 engine/src/flutter/build/android/pylib/OWNERS create mode 100644 engine/src/flutter/build/android/pylib/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/android_commands.py create mode 100644 engine/src/flutter/build/android/pylib/android_commands_unittest.py create mode 100644 engine/src/flutter/build/android/pylib/base/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/base/base_setup.py create mode 100644 engine/src/flutter/build/android/pylib/base/base_test_result.py create mode 100644 engine/src/flutter/build/android/pylib/base/base_test_result_unittest.py create mode 100644 engine/src/flutter/build/android/pylib/base/base_test_runner.py create mode 100644 engine/src/flutter/build/android/pylib/base/environment.py create mode 100644 engine/src/flutter/build/android/pylib/base/environment_factory.py create mode 100644 engine/src/flutter/build/android/pylib/base/test_collection.py create mode 100644 engine/src/flutter/build/android/pylib/base/test_dispatcher.py create mode 100755 engine/src/flutter/build/android/pylib/base/test_dispatcher_unittest.py create mode 100644 engine/src/flutter/build/android/pylib/base/test_instance.py create mode 100644 engine/src/flutter/build/android/pylib/base/test_instance_factory.py create mode 100644 engine/src/flutter/build/android/pylib/base/test_run.py create mode 100644 engine/src/flutter/build/android/pylib/base/test_run_factory.py create mode 100644 engine/src/flutter/build/android/pylib/base/test_server.py create mode 100644 engine/src/flutter/build/android/pylib/chrome_test_server_spawner.py create mode 100644 engine/src/flutter/build/android/pylib/cmd_helper.py create mode 100644 engine/src/flutter/build/android/pylib/cmd_helper_test.py create mode 100644 engine/src/flutter/build/android/pylib/constants/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/constants/keyevent.py create mode 100644 engine/src/flutter/build/android/pylib/content_settings.py create mode 100644 engine/src/flutter/build/android/pylib/device/OWNERS create mode 100644 engine/src/flutter/build/android/pylib/device/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/device/adb_wrapper.py create mode 100644 engine/src/flutter/build/android/pylib/device/adb_wrapper_test.py create mode 100644 engine/src/flutter/build/android/pylib/device/battery_utils.py create mode 100755 engine/src/flutter/build/android/pylib/device/battery_utils_test.py create mode 100644 engine/src/flutter/build/android/pylib/device/commands/BUILD.gn create mode 100644 engine/src/flutter/build/android/pylib/device/commands/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/device/commands/commands.gyp create mode 100644 engine/src/flutter/build/android/pylib/device/commands/install_commands.py create mode 100644 engine/src/flutter/build/android/pylib/device/commands/java/src/org/chromium/android/commands/unzip/Unzip.java create mode 100644 engine/src/flutter/build/android/pylib/device/decorators.py create mode 100644 engine/src/flutter/build/android/pylib/device/decorators_test.py create mode 100644 engine/src/flutter/build/android/pylib/device/device_blacklist.py create mode 100644 engine/src/flutter/build/android/pylib/device/device_errors.py create mode 100644 engine/src/flutter/build/android/pylib/device/device_list.py create mode 100644 engine/src/flutter/build/android/pylib/device/device_utils.py create mode 100755 engine/src/flutter/build/android/pylib/device/device_utils_device_test.py create mode 100755 engine/src/flutter/build/android/pylib/device/device_utils_test.py create mode 100644 engine/src/flutter/build/android/pylib/device/intent.py create mode 100644 engine/src/flutter/build/android/pylib/device/logcat_monitor.py create mode 100755 engine/src/flutter/build/android/pylib/device/logcat_monitor_test.py create mode 100644 engine/src/flutter/build/android/pylib/device/shared_prefs.py create mode 100755 engine/src/flutter/build/android/pylib/device/shared_prefs_test.py create mode 100644 engine/src/flutter/build/android/pylib/device_settings.py create mode 100644 engine/src/flutter/build/android/pylib/device_signal.py create mode 100755 engine/src/flutter/build/android/pylib/efficient_android_directory_copy.sh create mode 100644 engine/src/flutter/build/android/pylib/flag_changer.py create mode 100644 engine/src/flutter/build/android/pylib/forwarder.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/OWNERS create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/base_unittests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/base_unittests_emulator_additional_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/blink_heap_unittests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/breakpad_unittests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/cc_unittests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/content_browsertests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/content_unittests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/gfx_unittests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/ipc_tests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/media_unittests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/net_unittests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/sync_unit_tests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/unit_tests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/filter/webkit_unit_tests_disabled create mode 100644 engine/src/flutter/build/android/pylib/gtest/gtest_config.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/gtest_test_instance.py create mode 100755 engine/src/flutter/build/android/pylib/gtest/gtest_test_instance_test.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/local_device_gtest_run.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/setup.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/test_options.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/test_package.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/test_package_apk.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/test_package_exe.py create mode 100644 engine/src/flutter/build/android/pylib/gtest/test_runner.py create mode 100644 engine/src/flutter/build/android/pylib/host_driven/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/host_driven/setup.py create mode 100644 engine/src/flutter/build/android/pylib/host_driven/test_case.py create mode 100644 engine/src/flutter/build/android/pylib/host_driven/test_info_collection.py create mode 100644 engine/src/flutter/build/android/pylib/host_driven/test_runner.py create mode 100644 engine/src/flutter/build/android/pylib/host_driven/test_server.py create mode 100644 engine/src/flutter/build/android/pylib/host_driven/tests_annotations.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/instrumentation_parser.py create mode 100755 engine/src/flutter/build/android/pylib/instrumentation/instrumentation_parser_test.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/instrumentation_test_instance.py create mode 100755 engine/src/flutter/build/android/pylib/instrumentation/instrumentation_test_instance_test.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/json_perf_parser.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/setup.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/test_jar.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/test_options.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/test_package.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/test_result.py create mode 100644 engine/src/flutter/build/android/pylib/instrumentation/test_runner.py create mode 100644 engine/src/flutter/build/android/pylib/junit/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/junit/setup.py create mode 100644 engine/src/flutter/build/android/pylib/junit/test_dispatcher.py create mode 100644 engine/src/flutter/build/android/pylib/junit/test_runner.py create mode 100644 engine/src/flutter/build/android/pylib/linker/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/linker/setup.py create mode 100644 engine/src/flutter/build/android/pylib/linker/test_case.py create mode 100644 engine/src/flutter/build/android/pylib/linker/test_runner.py create mode 100644 engine/src/flutter/build/android/pylib/local/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/local/device/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/local/device/local_device_environment.py create mode 100644 engine/src/flutter/build/android/pylib/local/device/local_device_instrumentation_test_run.py create mode 100644 engine/src/flutter/build/android/pylib/local/device/local_device_test_run.py create mode 100644 engine/src/flutter/build/android/pylib/local/local_test_server_spawner.py create mode 100644 engine/src/flutter/build/android/pylib/monkey/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/monkey/setup.py create mode 100644 engine/src/flutter/build/android/pylib/monkey/test_options.py create mode 100644 engine/src/flutter/build/android/pylib/monkey/test_runner.py create mode 100644 engine/src/flutter/build/android/pylib/perf/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/perf/cache_control.py create mode 100644 engine/src/flutter/build/android/pylib/perf/perf_control.py create mode 100644 engine/src/flutter/build/android/pylib/perf/perf_control_unittest.py create mode 100644 engine/src/flutter/build/android/pylib/perf/setup.py create mode 100644 engine/src/flutter/build/android/pylib/perf/surface_stats_collector.py create mode 100644 engine/src/flutter/build/android/pylib/perf/surface_stats_collector_unittest.py create mode 100644 engine/src/flutter/build/android/pylib/perf/test_options.py create mode 100644 engine/src/flutter/build/android/pylib/perf/test_runner.py create mode 100644 engine/src/flutter/build/android/pylib/perf/thermal_throttle.py create mode 100644 engine/src/flutter/build/android/pylib/pexpect.py create mode 100644 engine/src/flutter/build/android/pylib/ports.py create mode 100644 engine/src/flutter/build/android/pylib/remote/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/remote/device/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/remote/device/appurify_constants.py create mode 100644 engine/src/flutter/build/android/pylib/remote/device/appurify_sanitized.py create mode 100644 engine/src/flutter/build/android/pylib/remote/device/dummy/BUILD.gn create mode 100644 engine/src/flutter/build/android/pylib/remote/device/dummy/dummy.gyp create mode 100644 engine/src/flutter/build/android/pylib/remote/device/dummy/src/org/chromium/dummy/Dummy.java create mode 100644 engine/src/flutter/build/android/pylib/remote/device/remote_device_environment.py create mode 100644 engine/src/flutter/build/android/pylib/remote/device/remote_device_gtest_run.py create mode 100644 engine/src/flutter/build/android/pylib/remote/device/remote_device_helper.py create mode 100644 engine/src/flutter/build/android/pylib/remote/device/remote_device_instrumentation_test_run.py create mode 100644 engine/src/flutter/build/android/pylib/remote/device/remote_device_test_run.py create mode 100644 engine/src/flutter/build/android/pylib/remote/device/remote_device_uirobot_test_run.py create mode 100755 engine/src/flutter/build/android/pylib/restart_adbd.sh create mode 100644 engine/src/flutter/build/android/pylib/results/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/results/flakiness_dashboard/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/results/flakiness_dashboard/json_results_generator.py create mode 100644 engine/src/flutter/build/android/pylib/results/flakiness_dashboard/results_uploader.py create mode 100644 engine/src/flutter/build/android/pylib/results/json_results.py create mode 100755 engine/src/flutter/build/android/pylib/results/json_results_test.py create mode 100644 engine/src/flutter/build/android/pylib/results/report_results.py create mode 100644 engine/src/flutter/build/android/pylib/screenshot.py create mode 100644 engine/src/flutter/build/android/pylib/sdk/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/sdk/dexdump.py create mode 100644 engine/src/flutter/build/android/pylib/sdk/split_select.py create mode 100644 engine/src/flutter/build/android/pylib/symbols/PRESUBMIT.py create mode 100644 engine/src/flutter/build/android/pylib/symbols/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/symbols/elf_symbolizer.py create mode 100755 engine/src/flutter/build/android/pylib/symbols/elf_symbolizer_unittest.py create mode 100644 engine/src/flutter/build/android/pylib/symbols/mock_addr2line/__init__.py create mode 100755 engine/src/flutter/build/android/pylib/symbols/mock_addr2line/mock_addr2line create mode 100644 engine/src/flutter/build/android/pylib/system_properties.py create mode 100644 engine/src/flutter/build/android/pylib/uiautomator/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/uiautomator/setup.py create mode 100644 engine/src/flutter/build/android/pylib/uiautomator/test_options.py create mode 100644 engine/src/flutter/build/android/pylib/uiautomator/test_package.py create mode 100644 engine/src/flutter/build/android/pylib/uiautomator/test_runner.py create mode 100644 engine/src/flutter/build/android/pylib/uirobot/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/uirobot/uirobot_test_instance.py create mode 100644 engine/src/flutter/build/android/pylib/utils/__init__.py create mode 100644 engine/src/flutter/build/android/pylib/utils/apk_helper.py create mode 100644 engine/src/flutter/build/android/pylib/utils/base_error.py create mode 100644 engine/src/flutter/build/android/pylib/utils/command_option_parser.py create mode 100644 engine/src/flutter/build/android/pylib/utils/device_temp_file.py create mode 100755 engine/src/flutter/build/android/pylib/utils/device_temp_file_test.py create mode 100644 engine/src/flutter/build/android/pylib/utils/emulator.py create mode 100644 engine/src/flutter/build/android/pylib/utils/findbugs.py create mode 100644 engine/src/flutter/build/android/pylib/utils/host_path_finder.py create mode 100644 engine/src/flutter/build/android/pylib/utils/host_utils.py create mode 100644 engine/src/flutter/build/android/pylib/utils/isolator.py create mode 100644 engine/src/flutter/build/android/pylib/utils/json_results_generator_unittest.py create mode 100644 engine/src/flutter/build/android/pylib/utils/logging_utils.py create mode 100644 engine/src/flutter/build/android/pylib/utils/md5sum.py create mode 100755 engine/src/flutter/build/android/pylib/utils/md5sum_test.py create mode 100644 engine/src/flutter/build/android/pylib/utils/mock_calls.py create mode 100755 engine/src/flutter/build/android/pylib/utils/mock_calls_test.py create mode 100644 engine/src/flutter/build/android/pylib/utils/parallelizer.py create mode 100644 engine/src/flutter/build/android/pylib/utils/parallelizer_test.py create mode 100644 engine/src/flutter/build/android/pylib/utils/proguard.py create mode 100644 engine/src/flutter/build/android/pylib/utils/repo_utils.py create mode 100644 engine/src/flutter/build/android/pylib/utils/reraiser_thread.py create mode 100644 engine/src/flutter/build/android/pylib/utils/reraiser_thread_unittest.py create mode 100644 engine/src/flutter/build/android/pylib/utils/run_tests_helper.py create mode 100644 engine/src/flutter/build/android/pylib/utils/test_environment.py create mode 100644 engine/src/flutter/build/android/pylib/utils/time_profile.py create mode 100644 engine/src/flutter/build/android/pylib/utils/timeout_retry.py create mode 100644 engine/src/flutter/build/android/pylib/utils/timeout_retry_unittest.py create mode 100644 engine/src/flutter/build/android/pylib/utils/watchdog_timer.py create mode 100644 engine/src/flutter/build/android/pylib/utils/xvfb.py create mode 100644 engine/src/flutter/build/android/pylib/utils/zip_utils.py create mode 100644 engine/src/flutter/build/android/pylib/valgrind_tools.py create mode 100644 engine/src/flutter/build/android/rezip.gyp create mode 100644 engine/src/flutter/build/android/rezip/BUILD.gn create mode 100644 engine/src/flutter/build/android/rezip/RezipApk.java create mode 100755 engine/src/flutter/build/android/screenshot.py create mode 100644 engine/src/flutter/build/android/setup.gyp create mode 100755 engine/src/flutter/build/android/symbolize.py create mode 100755 engine/src/flutter/build/android/symbolize_test.py create mode 100755 engine/src/flutter/build/android/test_runner.py create mode 100644 engine/src/flutter/build/android/tests/multiple_proguards/AndroidManifest.xml create mode 100644 engine/src/flutter/build/android/tests/multiple_proguards/multiple_proguards.gyp create mode 100644 engine/src/flutter/build/android/tests/multiple_proguards/proguard1.flags create mode 100644 engine/src/flutter/build/android/tests/multiple_proguards/proguard2.flags create mode 100644 engine/src/flutter/build/android/tests/multiple_proguards/src/dummy/DummyActivity.java create mode 100644 engine/src/flutter/build/android/tests/multiple_proguards/src/dummy/NativeLibraries.java create mode 100644 engine/src/flutter/build/android/tests/symbolize/Makefile create mode 100644 engine/src/flutter/build/android/tests/symbolize/a.cc create mode 100644 engine/src/flutter/build/android/tests/symbolize/b.cc create mode 100755 engine/src/flutter/build/android/tombstones.py create mode 100755 engine/src/flutter/build/android/update_verification.py create mode 100644 engine/src/flutter/build/android_sdk_extras.json create mode 100755 engine/src/flutter/build/apply_locales.py create mode 100755 engine/src/flutter/build/branding_value.sh create mode 100755 engine/src/flutter/build/build-ctags.sh create mode 100644 engine/src/flutter/build/build_config.h create mode 100755 engine/src/flutter/build/check_return_value.py create mode 100644 engine/src/flutter/build/common.croc create mode 100644 engine/src/flutter/build/compiled_action.gni create mode 100755 engine/src/flutter/build/compiler_version.py create mode 100644 engine/src/flutter/build/config/BUILD.gn create mode 100644 engine/src/flutter/build/config/BUILDCONFIG.gn create mode 100644 engine/src/flutter/build/config/OWNERS create mode 100644 engine/src/flutter/build/config/allocator.gni create mode 100644 engine/src/flutter/build/config/android/BUILD.gn create mode 100644 engine/src/flutter/build/config/android/OWNERS create mode 100644 engine/src/flutter/build/config/android/config.gni create mode 100644 engine/src/flutter/build/config/android/internal_rules.gni create mode 100644 engine/src/flutter/build/config/android/rules.gni create mode 100644 engine/src/flutter/build/config/arm.gni create mode 100644 engine/src/flutter/build/config/chrome_build.gni create mode 100644 engine/src/flutter/build/config/clang/BUILD.gn create mode 100644 engine/src/flutter/build/config/clang/clang.gni create mode 100644 engine/src/flutter/build/config/compiler/BUILD.gn create mode 100644 engine/src/flutter/build/config/crypto.gni create mode 100644 engine/src/flutter/build/config/features.gni create mode 100644 engine/src/flutter/build/config/gcc/BUILD.gn create mode 100644 engine/src/flutter/build/config/gcc/gcc_version.gni create mode 100644 engine/src/flutter/build/config/ios/BUILD.gn create mode 100644 engine/src/flutter/build/config/ios/ios_sdk.gni create mode 100644 engine/src/flutter/build/config/ios/ios_sdk.py create mode 100644 engine/src/flutter/build/config/linux/BUILD.gn create mode 100644 engine/src/flutter/build/config/linux/gtk/BUILD.gn create mode 100644 engine/src/flutter/build/config/linux/pkg-config.py create mode 100644 engine/src/flutter/build/config/linux/pkg_config.gni create mode 100644 engine/src/flutter/build/config/linux/sysroot_ld_path.py create mode 100644 engine/src/flutter/build/config/locales.gni create mode 100644 engine/src/flutter/build/config/mac/BUILD.gn create mode 100644 engine/src/flutter/build/config/mac/mac_sdk.gni create mode 100644 engine/src/flutter/build/config/mips.gni create mode 100644 engine/src/flutter/build/config/sanitizers/BUILD.gn create mode 100644 engine/src/flutter/build/config/sanitizers/sanitizers.gni create mode 100644 engine/src/flutter/build/config/sysroot.gni create mode 100644 engine/src/flutter/build/config/ui.gni create mode 100644 engine/src/flutter/build/config/win/BUILD.gn create mode 100644 engine/src/flutter/build/config/win/visual_studio_version.gni create mode 100755 engine/src/flutter/build/copy_test_data_ios.py create mode 100755 engine/src/flutter/build/cp.py create mode 100755 engine/src/flutter/build/detect_host_arch.py create mode 100755 engine/src/flutter/build/dir_exists.py create mode 100755 engine/src/flutter/build/download_gold_plugin.py create mode 100755 engine/src/flutter/build/download_nacl_toolchains.py create mode 100755 engine/src/flutter/build/download_sdk_extras.py create mode 100755 engine/src/flutter/build/env_dump.py create mode 100755 engine/src/flutter/build/extract_from_cab.py create mode 100755 engine/src/flutter/build/find_isolated_tests.py create mode 100755 engine/src/flutter/build/gdb-add-index create mode 100755 engine/src/flutter/build/get_landmines.py create mode 100755 engine/src/flutter/build/get_sdk_extras_packages.py create mode 100755 engine/src/flutter/build/get_syzygy_binaries.py create mode 100644 engine/src/flutter/build/git-hooks/OWNERS create mode 100755 engine/src/flutter/build/git-hooks/pre-commit create mode 100644 engine/src/flutter/build/gn_helpers.py create mode 100644 engine/src/flutter/build/gn_run_binary.py create mode 100755 engine/src/flutter/build/gyp_chromium create mode 100644 engine/src/flutter/build/gyp_chromium.py create mode 100755 engine/src/flutter/build/gyp_chromium_test.py.remove create mode 100644 engine/src/flutter/build/gyp_environment.py create mode 100644 engine/src/flutter/build/gyp_helper.py create mode 100644 engine/src/flutter/build/gypi_to_gn.py create mode 100755 engine/src/flutter/build/install-android-sdks.sh create mode 100755 engine/src/flutter/build/install-build-deps-android.sh create mode 100755 engine/src/flutter/build/install-build-deps-mac.sh create mode 100755 engine/src/flutter/build/install-build-deps.sh create mode 100755 engine/src/flutter/build/install-chroot.sh create mode 100644 engine/src/flutter/build/internal/README.chromium create mode 100755 engine/src/flutter/build/inverse_depth.py create mode 100644 engine/src/flutter/build/ios/OWNERS create mode 100644 engine/src/flutter/build/ios/PRESUBMIT.py create mode 100644 engine/src/flutter/build/ios/chrome_ios.croc create mode 100755 engine/src/flutter/build/ios/clean_env.py create mode 100644 engine/src/flutter/build/ios/grit_whitelist.txt create mode 100644 engine/src/flutter/build/json_schema_api.gni create mode 100644 engine/src/flutter/build/landmine_utils.py create mode 100755 engine/src/flutter/build/landmines.py create mode 100755 engine/src/flutter/build/ls.py create mode 100644 engine/src/flutter/build/module_args/mojo.gni create mode 100644 engine/src/flutter/build/module_args/v8.gni create mode 100644 engine/src/flutter/build/output_dll_copy.rules create mode 100644 engine/src/flutter/build/precompile.cc create mode 100644 engine/src/flutter/build/precompile.h create mode 100755 engine/src/flutter/build/protoc_java.py create mode 100755 engine/src/flutter/build/rmdir_and_stamp.py create mode 100644 engine/src/flutter/build/sanitize-mac-build-log.sed create mode 100755 engine/src/flutter/build/sanitize-mac-build-log.sh create mode 100644 engine/src/flutter/build/sanitize-win-build-log.sed create mode 100755 engine/src/flutter/build/sanitize-win-build-log.sh create mode 100644 engine/src/flutter/build/sanitizers/BUILD.gn create mode 100644 engine/src/flutter/build/sanitizers/OWNERS create mode 100644 engine/src/flutter/build/sanitizers/asan_suppressions.cc create mode 100644 engine/src/flutter/build/sanitizers/lsan_suppressions.cc create mode 100644 engine/src/flutter/build/sanitizers/sanitizer_options.cc create mode 100644 engine/src/flutter/build/sanitizers/sanitizers.gyp create mode 100644 engine/src/flutter/build/sanitizers/tsan_suppressions.cc create mode 100644 engine/src/flutter/build/secondary/testing/gmock/BUILD.gn create mode 100644 engine/src/flutter/build/secondary/testing/gtest/BUILD.gn create mode 100644 engine/src/flutter/build/secondary/third_party/android_tools/BUILD.gn create mode 100644 engine/src/flutter/build/secondary/third_party/cacheinvalidation/BUILD.gn create mode 100644 engine/src/flutter/build/secondary/third_party/cacheinvalidation/src/google/cacheinvalidation/BUILD.gn create mode 100644 engine/src/flutter/build/secondary/third_party/libjpeg_turbo/BUILD.gn create mode 100644 engine/src/flutter/build/secondary/third_party/libsrtp/BUILD.gn create mode 100644 engine/src/flutter/build/secondary/third_party/nss/BUILD.gn create mode 100644 engine/src/flutter/build/secondary/tools/grit/BUILD.gn create mode 100644 engine/src/flutter/build/secondary/tools/grit/grit_rule.gni create mode 100644 engine/src/flutter/build/secondary/tools/grit/repack.gni create mode 100644 engine/src/flutter/build/secondary/tools/grit/stamp_grit_sources.py create mode 100644 engine/src/flutter/build/slave/OWNERS create mode 100644 engine/src/flutter/build/slave/README create mode 100644 engine/src/flutter/build/some.gyp create mode 100755 engine/src/flutter/build/symlink.py create mode 100644 engine/src/flutter/build/temp_gyp/README.chromium create mode 100644 engine/src/flutter/build/temp_gyp/pdfsqueeze.gyp create mode 100644 engine/src/flutter/build/toolchain/OWNERS create mode 100644 engine/src/flutter/build/toolchain/android/BUILD.gn create mode 100644 engine/src/flutter/build/toolchain/ccache.gni create mode 100644 engine/src/flutter/build/toolchain/clang.gni create mode 100644 engine/src/flutter/build/toolchain/cros/BUILD.gn create mode 100644 engine/src/flutter/build/toolchain/gcc_toolchain.gni create mode 100644 engine/src/flutter/build/toolchain/get_concurrent_links.py create mode 100644 engine/src/flutter/build/toolchain/goma.gni create mode 100644 engine/src/flutter/build/toolchain/linux/BUILD.gn create mode 100644 engine/src/flutter/build/toolchain/mac/BUILD.gn create mode 100644 engine/src/flutter/build/toolchain/mac/setup_toolchain.py create mode 100644 engine/src/flutter/build/toolchain/nacl/BUILD.gn create mode 100644 engine/src/flutter/build/toolchain/win/BUILD.gn create mode 100644 engine/src/flutter/build/toolchain/win/midl.gni create mode 100644 engine/src/flutter/build/toolchain/win/setup_toolchain.py create mode 100755 engine/src/flutter/build/tree_truth.sh create mode 100755 engine/src/flutter/build/update-linux-sandbox.sh create mode 100644 engine/src/flutter/build/util/BUILD.gn create mode 100755 engine/src/flutter/build/util/lastchange.py create mode 100644 engine/src/flutter/build/util/lib/common/__init__.py create mode 100644 engine/src/flutter/build/util/lib/common/perf_result_data_type.py create mode 100644 engine/src/flutter/build/util/lib/common/perf_tests_results_helper.py create mode 100644 engine/src/flutter/build/util/lib/common/unittest_util.py create mode 100644 engine/src/flutter/build/util/lib/common/util.py create mode 100755 engine/src/flutter/build/util/version.py create mode 100644 engine/src/flutter/build/vs_toolchain.py create mode 100644 engine/src/flutter/build/whitespace_file.txt create mode 100755 engine/src/flutter/build/win_is_xtree_patched.py create mode 100644 engine/src/flutter/mojom/BUILD.gn create mode 100644 engine/src/flutter/mojom/lexer.cc create mode 100644 engine/src/flutter/mojom/lexer.h create mode 100644 engine/src/flutter/mojom/lexer_unittest.cc create mode 100644 engine/src/flutter/mojom/mojom.ebnf create mode 100644 engine/src/flutter/skia/BUILD.gn create mode 100644 engine/src/flutter/skia/OWNERS create mode 100644 engine/src/flutter/skia/config/SkUserConfig.h create mode 100644 engine/src/flutter/skia/config/sk_ref_cnt_ext_debug.h create mode 100644 engine/src/flutter/skia/config/sk_ref_cnt_ext_release.h create mode 100644 engine/src/flutter/skia/skia.gyp create mode 100644 engine/src/flutter/skia/skia_Prefix.pch create mode 100644 engine/src/flutter/skia/skia_library_opts.gyp create mode 100644 engine/src/flutter/skia/skia_test_expectations.txt create mode 100644 engine/src/flutter/skia/skia_tests.gyp create mode 100644 engine/src/flutter/skia/tools/filter_fuzz_stub/filter_fuzz_stub.cc create mode 100644 engine/src/flutter/testing/BUILD.gn create mode 100644 engine/src/flutter/testing/OWNERS create mode 100644 engine/src/flutter/testing/PRESUBMIT.py create mode 100644 engine/src/flutter/testing/android/OWNERS create mode 100644 engine/src/flutter/testing/android/appurify_support.gyp create mode 100644 engine/src/flutter/testing/android/appurify_support/BUILD.gn create mode 100644 engine/src/flutter/testing/android/appurify_support/java/src/org/chromium/test/support/ResultsBundleGenerator.java create mode 100644 engine/src/flutter/testing/android/appurify_support/java/src/org/chromium/test/support/RobotiumBundleGenerator.java create mode 100644 engine/src/flutter/testing/android/broker/BUILD.gn create mode 100644 engine/src/flutter/testing/android/broker/java/src/org/chromium/test/broker/OnDeviceInstrumentationBroker.java create mode 100644 engine/src/flutter/testing/android/driver/BUILD.gn create mode 100644 engine/src/flutter/testing/android/driver/java/AndroidManifest.xml create mode 100644 engine/src/flutter/testing/android/driver/java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java create mode 100644 engine/src/flutter/testing/android/junit/BUILD.gn create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestComputer.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestFilter.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestListener.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestLogger.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JsonListener.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JsonLogger.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/LocalRobolectricTestRunner.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/PackageFilter.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/RobolectricClasspathDependencyResolver.java create mode 100644 engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/RunnerFilter.java create mode 100644 engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/GtestFilterTest.java create mode 100644 engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/GtestLoggerTest.java create mode 100644 engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/PackageFilterTest.java create mode 100644 engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/RunnerFilterTest.java create mode 100644 engine/src/flutter/testing/android/junit/junit_test.gyp create mode 100644 engine/src/flutter/testing/android/native_test.gyp create mode 100644 engine/src/flutter/testing/android/native_test/BUILD.gn create mode 100644 engine/src/flutter/testing/android/native_test/README.chromium create mode 100644 engine/src/flutter/testing/android/native_test/java/AndroidManifest.xml create mode 100644 engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java create mode 100644 engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeTestActivity.java create mode 100644 engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java create mode 100644 engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeUnitTestActivity.java create mode 100644 engine/src/flutter/testing/android/native_test/native_test_jni_onload.cc create mode 100644 engine/src/flutter/testing/android/native_test/native_test_launcher.cc create mode 100644 engine/src/flutter/testing/android/native_test/native_test_launcher.h create mode 100644 engine/src/flutter/testing/android/native_test/native_test_util.cc create mode 100644 engine/src/flutter/testing/android/native_test/native_test_util.h create mode 100644 engine/src/flutter/testing/android/on_device_instrumentation.gyp create mode 100644 engine/src/flutter/testing/android/reporter/BUILD.gn create mode 100644 engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusListener.java create mode 100644 engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusReceiver.java create mode 100644 engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusReporter.java create mode 100644 engine/src/flutter/testing/buildbot/OWNERS create mode 100644 engine/src/flutter/testing/buildbot/PRESUBMIT.py create mode 100644 engine/src/flutter/testing/buildbot/chromium.chrome.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.chromiumos.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.fyi.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.linux.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.mac.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.memory.fyi.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.memory.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.perf.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.webkit.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.webrtc.fyi.json create mode 100644 engine/src/flutter/testing/buildbot/chromium.win.json create mode 100644 engine/src/flutter/testing/buildbot/chromium_arm.json create mode 100644 engine/src/flutter/testing/buildbot/chromium_memory_trybot.json create mode 100644 engine/src/flutter/testing/buildbot/chromium_trybot.json create mode 100644 engine/src/flutter/testing/buildbot/chromium_win8_trybot.json create mode 100644 engine/src/flutter/testing/buildbot/client.v8.branches.json create mode 100644 engine/src/flutter/testing/buildbot/client.v8.fyi.json create mode 100755 engine/src/flutter/testing/buildbot/manage.py create mode 100644 engine/src/flutter/testing/buildbot/trybot_analyze_config.json create mode 100644 engine/src/flutter/testing/buildbot/tryserver.blink.json create mode 100644 engine/src/flutter/testing/buildbot/tryserver.chromium.linux.json create mode 100644 engine/src/flutter/testing/buildbot/tryserver.chromium.mac.json create mode 100644 engine/src/flutter/testing/buildbot/tryserver.chromium.perf.json create mode 100644 engine/src/flutter/testing/buildbot/tryserver.chromium.win.json create mode 100644 engine/src/flutter/testing/buildbot/tryserver.v8.json create mode 100644 engine/src/flutter/testing/chromoting/browser_test_commands_linux.txt create mode 100644 engine/src/flutter/testing/chromoting/browser_tests_launcher.py create mode 100644 engine/src/flutter/testing/chromoting/chromoting_integration_tests.isolate create mode 100644 engine/src/flutter/testing/chromoting/integration_tests.gyp create mode 100644 engine/src/flutter/testing/chromoting/multi_machine_example/example_task.isolate create mode 100644 engine/src/flutter/testing/chromoting/multi_machine_example/example_test_controller.isolate create mode 100755 engine/src/flutter/testing/chromoting/multi_machine_example/example_test_controller.py create mode 100644 engine/src/flutter/testing/coverage_util_ios.cc create mode 100644 engine/src/flutter/testing/coverage_util_ios.h create mode 100755 engine/src/flutter/testing/generate_gmock_mutant.py create mode 100644 engine/src/flutter/testing/gmock.gyp create mode 100644 engine/src/flutter/testing/gmock_mutant.h create mode 100644 engine/src/flutter/testing/gtest.gyp create mode 100644 engine/src/flutter/testing/gtest_ios/Default.png create mode 100755 engine/src/flutter/testing/gtest_ios/run-unittest.sh create mode 100644 engine/src/flutter/testing/gtest_ios/unittest-Info.plist create mode 100644 engine/src/flutter/testing/gtest_mac.h create mode 100644 engine/src/flutter/testing/gtest_mac.mm create mode 100644 engine/src/flutter/testing/gtest_mac_unittest.mm create mode 100644 engine/src/flutter/testing/gtest_nacl.gyp create mode 100644 engine/src/flutter/testing/iossim/OWNERS create mode 100644 engine/src/flutter/testing/iossim/iossim.gyp create mode 100644 engine/src/flutter/testing/iossim/iossim.mm create mode 100755 engine/src/flutter/testing/iossim/redirect-stdout.sh create mode 100644 engine/src/flutter/testing/legion/__init__.py create mode 100644 engine/src/flutter/testing/legion/common_lib.py create mode 100644 engine/src/flutter/testing/legion/examples/hello_world/controller_test.isolate create mode 100755 engine/src/flutter/testing/legion/examples/hello_world/controller_test.py create mode 100644 engine/src/flutter/testing/legion/examples/hello_world/task_test.isolate create mode 100755 engine/src/flutter/testing/legion/examples/hello_world/task_test.py create mode 100644 engine/src/flutter/testing/legion/examples/subprocess/subprocess_test.isolate create mode 100755 engine/src/flutter/testing/legion/examples/subprocess/subprocess_test.py create mode 100644 engine/src/flutter/testing/legion/examples/subprocess/task.isolate create mode 100644 engine/src/flutter/testing/legion/legion.isolate create mode 100644 engine/src/flutter/testing/legion/legion_test_case.py create mode 100644 engine/src/flutter/testing/legion/process.py create mode 100644 engine/src/flutter/testing/legion/rpc_methods.py create mode 100644 engine/src/flutter/testing/legion/rpc_server.py create mode 100755 engine/src/flutter/testing/legion/run_task.py create mode 100644 engine/src/flutter/testing/legion/task_controller.py create mode 100644 engine/src/flutter/testing/legion/task_registration_server.py create mode 100644 engine/src/flutter/testing/legion/test_controller.py create mode 100755 engine/src/flutter/testing/legion/tools/legion.py create mode 100644 engine/src/flutter/testing/multiprocess_func_list.cc create mode 100644 engine/src/flutter/testing/multiprocess_func_list.h create mode 100644 engine/src/flutter/testing/perf/BUILD.gn create mode 100644 engine/src/flutter/testing/perf/perf_test.cc create mode 100644 engine/src/flutter/testing/perf/perf_test.gyp create mode 100644 engine/src/flutter/testing/perf/perf_test.h create mode 100644 engine/src/flutter/testing/platform_test.h create mode 100644 engine/src/flutter/testing/platform_test_mac.mm create mode 100644 engine/src/flutter/testing/scripts/OWNERS create mode 100755 engine/src/flutter/testing/scripts/checkdeps.py create mode 100755 engine/src/flutter/testing/scripts/checklicenses.py create mode 100755 engine/src/flutter/testing/scripts/checkperms.py create mode 100644 engine/src/flutter/testing/scripts/common.py create mode 100755 engine/src/flutter/testing/scripts/get_compile_targets.py create mode 100755 engine/src/flutter/testing/scripts/gn_check.py create mode 100755 engine/src/flutter/testing/scripts/gtest_perf_test.py create mode 100755 engine/src/flutter/testing/scripts/gyp_flag_compare.py create mode 100755 engine/src/flutter/testing/scripts/host_info.py create mode 100755 engine/src/flutter/testing/scripts/mojo_apptest.py create mode 100755 engine/src/flutter/testing/scripts/nacl_integration.py create mode 100755 engine/src/flutter/testing/scripts/telemetry_perf_unittests.py create mode 100755 engine/src/flutter/testing/scripts/telemetry_unittests.py create mode 100755 engine/src/flutter/testing/scripts/webkit_lint.py create mode 100755 engine/src/flutter/testing/scripts/webkit_python_tests.py create mode 100755 engine/src/flutter/testing/scripts/webview_licenses.py create mode 100644 engine/src/flutter/testing/test.gni create mode 100755 engine/src/flutter/testing/test_env.py create mode 100644 engine/src/flutter/testing/variations/PRESUBMIT.py create mode 100644 engine/src/flutter/testing/variations/fieldtrial_testing_config.json create mode 100644 engine/src/flutter/testing/variations/fieldtrial_testing_config_win.json create mode 100755 engine/src/flutter/testing/xvfb.py create mode 100644 engine/src/flutter/third_party/android_testrunner/LICENSE create mode 100644 engine/src/flutter/third_party/android_testrunner/OWNERS create mode 100644 engine/src/flutter/third_party/android_testrunner/README.chromium create mode 100644 engine/src/flutter/third_party/android_testrunner/adb_interface.py create mode 100644 engine/src/flutter/third_party/android_testrunner/am_instrument_parser.py create mode 100644 engine/src/flutter/third_party/android_testrunner/errors.py create mode 100644 engine/src/flutter/third_party/android_testrunner/logger.py create mode 100644 engine/src/flutter/third_party/android_testrunner/patch.diff create mode 100644 engine/src/flutter/third_party/android_testrunner/run_command.py create mode 100644 engine/src/flutter/third_party/ashmem/BUILD.gn create mode 100644 engine/src/flutter/third_party/ashmem/LICENSE create mode 100644 engine/src/flutter/third_party/ashmem/OWNERS create mode 100644 engine/src/flutter/third_party/ashmem/README.chromium create mode 100644 engine/src/flutter/third_party/ashmem/ashmem-dev.c create mode 100644 engine/src/flutter/third_party/ashmem/ashmem.gyp create mode 100644 engine/src/flutter/third_party/ashmem/ashmem.h create mode 100644 engine/src/flutter/third_party/binutils/.gitignore create mode 100644 engine/src/flutter/third_party/binutils/LICENSE create mode 100644 engine/src/flutter/third_party/binutils/Linux_ia32/binutils.tar.bz2.sha1 create mode 100644 engine/src/flutter/third_party/binutils/Linux_x64/binutils.tar.bz2.sha1 create mode 100644 engine/src/flutter/third_party/binutils/OWNERS create mode 100644 engine/src/flutter/third_party/binutils/README.chromium create mode 100755 engine/src/flutter/third_party/binutils/build-all.sh create mode 100755 engine/src/flutter/third_party/binutils/build-one.sh create mode 100755 engine/src/flutter/third_party/binutils/download.py create mode 100644 engine/src/flutter/third_party/binutils/ehframe-race.patch create mode 100644 engine/src/flutter/third_party/binutils/plugin-dso-fix.patch create mode 100644 engine/src/flutter/third_party/binutils/unlock-thin.patch create mode 100755 engine/src/flutter/third_party/binutils/upload.sh create mode 100644 engine/src/flutter/third_party/dart-pkg/BUILD.gn create mode 100644 engine/src/flutter/third_party/freetype-android/BUILD.gn create mode 100644 engine/src/flutter/third_party/freetype-android/README.chromium create mode 100644 engine/src/flutter/third_party/iccjpeg/BUILD.gn create mode 100644 engine/src/flutter/third_party/iccjpeg/LICENSE create mode 100644 engine/src/flutter/third_party/iccjpeg/OWNERS create mode 100644 engine/src/flutter/third_party/iccjpeg/README.chromium create mode 100644 engine/src/flutter/third_party/iccjpeg/iccjpeg.c create mode 100644 engine/src/flutter/third_party/iccjpeg/iccjpeg.gyp create mode 100644 engine/src/flutter/third_party/iccjpeg/iccjpeg.h create mode 100644 engine/src/flutter/third_party/jsr-305/BUILD.gn create mode 100644 engine/src/flutter/third_party/jsr-305/README.chromium create mode 100644 engine/src/flutter/third_party/jsr-305/jsr-305.gyp create mode 100644 engine/src/flutter/third_party/junit/BUILD.gn create mode 100644 engine/src/flutter/third_party/junit/LICENSE create mode 100644 engine/src/flutter/third_party/junit/OWNERS create mode 100644 engine/src/flutter/third_party/junit/README.chromium create mode 100644 engine/src/flutter/third_party/junit/junit.gyp create mode 100644 engine/src/flutter/third_party/markupsafe/AUTHORS create mode 100644 engine/src/flutter/third_party/markupsafe/LICENSE create mode 100644 engine/src/flutter/third_party/markupsafe/MarkupSafe-0.18.tar.gz.md5 create mode 100644 engine/src/flutter/third_party/markupsafe/MarkupSafe-0.18.tar.gz.sha512 create mode 100644 engine/src/flutter/third_party/markupsafe/OWNERS create mode 100644 engine/src/flutter/third_party/markupsafe/README.chromium create mode 100644 engine/src/flutter/third_party/markupsafe/__init__.py create mode 100644 engine/src/flutter/third_party/markupsafe/_compat.py create mode 100644 engine/src/flutter/third_party/markupsafe/_constants.py create mode 100644 engine/src/flutter/third_party/markupsafe/_native.py create mode 100644 engine/src/flutter/third_party/markupsafe/_speedups.c create mode 100755 engine/src/flutter/third_party/markupsafe/get_markupsafe.sh create mode 100644 engine/src/flutter/third_party/mesa/BUILD.gn create mode 100644 engine/src/flutter/third_party/mesa/LICENSE create mode 100644 engine/src/flutter/third_party/mesa/OWNERS create mode 100644 engine/src/flutter/third_party/mesa/README.chromium create mode 100644 engine/src/flutter/third_party/mesa/README.txt create mode 100644 engine/src/flutter/third_party/mesa/chromium.patch create mode 100644 engine/src/flutter/third_party/mesa/generate_git_sha1.py create mode 100644 engine/src/flutter/third_party/mesa/mesa.gyp create mode 100644 engine/src/flutter/third_party/mesa/redirectoutput.py create mode 100644 engine/src/flutter/third_party/mockito/BUILD.gn create mode 100644 engine/src/flutter/third_party/mockito/LICENSE create mode 100644 engine/src/flutter/third_party/mockito/OWNERS create mode 100644 engine/src/flutter/third_party/mockito/README.chromium create mode 100644 engine/src/flutter/third_party/mockito/mockito.gyp create mode 100644 engine/src/flutter/third_party/modp_b64/BUILD.gn create mode 100644 engine/src/flutter/third_party/modp_b64/DEPS create mode 100644 engine/src/flutter/third_party/modp_b64/LICENSE create mode 100644 engine/src/flutter/third_party/modp_b64/OWNERS create mode 100644 engine/src/flutter/third_party/modp_b64/README.chromium create mode 100644 engine/src/flutter/third_party/modp_b64/modp_b64.cc create mode 100644 engine/src/flutter/third_party/modp_b64/modp_b64.gyp create mode 100644 engine/src/flutter/third_party/modp_b64/modp_b64.h create mode 100644 engine/src/flutter/third_party/modp_b64/modp_b64_data.h create mode 100644 engine/src/flutter/third_party/modp_b64/modp_b64_nacl.gyp create mode 100644 engine/src/flutter/third_party/ply/LICENSE create mode 100644 engine/src/flutter/third_party/ply/README create mode 100644 engine/src/flutter/third_party/ply/README.chromium create mode 100644 engine/src/flutter/third_party/ply/__init__.py create mode 100644 engine/src/flutter/third_party/ply/lex.py create mode 100644 engine/src/flutter/third_party/ply/license.patch create mode 100644 engine/src/flutter/third_party/ply/yacc.py create mode 100644 engine/src/flutter/third_party/pymock/LICENSE.txt create mode 100644 engine/src/flutter/third_party/pymock/OWNERS create mode 100644 engine/src/flutter/third_party/pymock/README.chromium create mode 100644 engine/src/flutter/third_party/pymock/mock.py create mode 100644 engine/src/flutter/third_party/robolectric/BUILD.gn create mode 100644 engine/src/flutter/third_party/robolectric/LICENSE create mode 100644 engine/src/flutter/third_party/robolectric/OWNERS create mode 100644 engine/src/flutter/third_party/robolectric/README.chromium create mode 100644 engine/src/flutter/third_party/robolectric/licenses/extreme.indiana.edu.license.txt create mode 100644 engine/src/flutter/third_party/robolectric/licenses/javolution.license.txt create mode 100644 engine/src/flutter/third_party/robolectric/licenses/pivotal.labs.license.txt create mode 100644 engine/src/flutter/third_party/robolectric/robolectric.gyp create mode 100644 engine/src/flutter/third_party/smhasher/BUILD.gn create mode 100644 engine/src/flutter/third_party/smhasher/LICENSE create mode 100644 engine/src/flutter/third_party/smhasher/README.chromium create mode 100644 engine/src/flutter/third_party/smhasher/smhasher.gyp create mode 100644 engine/src/flutter/tools/checkperms/OWNERS create mode 100644 engine/src/flutter/tools/checkperms/PRESUBMIT.py create mode 100755 engine/src/flutter/tools/checkperms/checkperms.py create mode 100755 engine/src/flutter/tools/dart/update.py create mode 100644 engine/src/flutter/tools/find_depot_tools.py create mode 100644 engine/src/flutter/tools/gdb/gdb_chrome.py create mode 100644 engine/src/flutter/tools/generate_library_loader/generate_library_loader.gni create mode 100755 engine/src/flutter/tools/generate_library_loader/generate_library_loader.py create mode 100644 engine/src/flutter/tools/git/README create mode 100755 engine/src/flutter/tools/git/for-all-touched-files.py create mode 100755 engine/src/flutter/tools/git/git-diff-ide.py create mode 100755 engine/src/flutter/tools/git/git-utils.sh create mode 100755 engine/src/flutter/tools/git/graph.sh create mode 100755 engine/src/flutter/tools/git/mass-rename.py create mode 100755 engine/src/flutter/tools/git/mass-rename.sh create mode 100755 engine/src/flutter/tools/git/mffr.py create mode 100755 engine/src/flutter/tools/git/move_source_file.bat create mode 100755 engine/src/flutter/tools/git/move_source_file.py create mode 100755 engine/src/flutter/tools/git/post-checkout create mode 100755 engine/src/flutter/tools/git/post-merge create mode 100755 engine/src/flutter/tools/git/update-copyrights.sh create mode 100644 engine/src/flutter/tools/go/VERSION_LINUX create mode 100644 engine/src/flutter/tools/go/VERSION_MACOSX create mode 100755 engine/src/flutter/tools/go/download.py create mode 100755 engine/src/flutter/tools/go/upload.py create mode 100644 engine/src/flutter/tools/gritsettings/resource_ids create mode 100755 engine/src/flutter/tools/gyp/pylib/gyp/mac_tool.py create mode 100644 engine/src/flutter/tools/idl_parser/PRESUBMIT.py create mode 100644 engine/src/flutter/tools/idl_parser/__init__.py create mode 100755 engine/src/flutter/tools/idl_parser/idl_lexer.py create mode 100755 engine/src/flutter/tools/idl_parser/idl_lexer_test.py create mode 100755 engine/src/flutter/tools/idl_parser/idl_node.py create mode 100755 engine/src/flutter/tools/idl_parser/idl_parser.py create mode 100755 engine/src/flutter/tools/idl_parser/idl_parser_test.py create mode 100755 engine/src/flutter/tools/idl_parser/idl_ppapi_lexer.py create mode 100755 engine/src/flutter/tools/idl_parser/idl_ppapi_parser.py create mode 100755 engine/src/flutter/tools/idl_parser/run_tests.py create mode 100755 engine/src/flutter/tools/licenses.py create mode 100644 engine/src/flutter/tools/linux/PRESUBMIT.py create mode 100755 engine/src/flutter/tools/linux/dump-static-initializers.py create mode 100755 engine/src/flutter/tools/linux/procfs.py create mode 100755 engine/src/flutter/tools/linux/tests/procfs_tests.py create mode 100644 engine/src/flutter/tools/memory/asan/blacklist.txt create mode 100755 engine/src/flutter/tools/protoc_wrapper/protoc_wrapper.py create mode 100644 engine/src/flutter/tools/relocation_packer/BUILD.gn create mode 100644 engine/src/flutter/tools/relocation_packer/LICENSE create mode 100644 engine/src/flutter/tools/relocation_packer/README.TXT create mode 100644 engine/src/flutter/tools/relocation_packer/config.gni create mode 100644 engine/src/flutter/tools/relocation_packer/relocation_packer.gyp create mode 100644 engine/src/flutter/tools/relocation_packer/src/debug.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/debug.h create mode 100644 engine/src/flutter/tools/relocation_packer/src/debug_unittest.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/delta_encoder.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/delta_encoder.h create mode 100644 engine/src/flutter/tools/relocation_packer/src/delta_encoder_unittest.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/elf_file.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/elf_file.h create mode 100644 engine/src/flutter/tools/relocation_packer/src/elf_file_unittest.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/elf_traits.h create mode 100644 engine/src/flutter/tools/relocation_packer/src/leb128.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/leb128.h create mode 100644 engine/src/flutter/tools/relocation_packer/src/leb128_unittest.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/main.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/packer.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/packer.h create mode 100644 engine/src/flutter/tools/relocation_packer/src/packer_unittest.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/run_all_unittests.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/run_length_encoder.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/run_length_encoder.h create mode 100644 engine/src/flutter/tools/relocation_packer/src/run_length_encoder_unittest.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/sleb128.cc create mode 100644 engine/src/flutter/tools/relocation_packer/src/sleb128.h create mode 100644 engine/src/flutter/tools/relocation_packer/src/sleb128_unittest.cc create mode 100644 engine/src/flutter/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc create mode 100755 engine/src/flutter/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py create mode 100755 engine/src/flutter/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh create mode 100755 engine/src/flutter/tools/remove_stale_pyc_files.py create mode 100644 engine/src/flutter/tools/resources/about_credits.tmpl create mode 100644 engine/src/flutter/tools/resources/about_credits_entry.tmpl create mode 100755 engine/src/flutter/tools/sort-headers.py create mode 100755 engine/src/flutter/tools/sort_sources.py create mode 100644 engine/src/flutter/tools/vim/chromium.ycm_extra_conf.py create mode 100644 engine/src/flutter/tools/vim/clang-format.vim create mode 100644 engine/src/flutter/tools/vim/filetypes.vim create mode 100644 engine/src/flutter/tools/vim/ninja-build.vim create mode 100644 engine/src/flutter/tools/vim/ninja_output.py create mode 100644 engine/src/flutter/tools/xdisplaycheck/BUILD.gn create mode 100644 engine/src/flutter/tools/xdisplaycheck/xdisplaycheck.cc create mode 100644 engine/src/flutter/tools/xdisplaycheck/xdisplaycheck.gyp create mode 100644 engine/src/flutter/tools/yes_no.py diff --git a/engine/src/flutter/build/OWNERS b/engine/src/flutter/build/OWNERS new file mode 100644 index 0000000000..17d067cff3 --- /dev/null +++ b/engine/src/flutter/build/OWNERS @@ -0,0 +1,5 @@ +cjhopman@chromium.org +dpranke@chromium.org +jochen@chromium.org +scottmg@chromium.org +thakis@chromium.org diff --git a/engine/src/flutter/build/PRESUBMIT.py b/engine/src/flutter/build/PRESUBMIT.py new file mode 100644 index 0000000000..fca962f1ca --- /dev/null +++ b/engine/src/flutter/build/PRESUBMIT.py @@ -0,0 +1,16 @@ +# Copyright 2015 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. + + +def _RunTests(input_api, output_api): + return (input_api.canned_checks.RunUnitTestsInDirectory( + input_api, output_api, '.', whitelist=[r'.+_test.py$'])) + + +def CheckChangeOnUpload(input_api, output_api): + return _RunTests(input_api, output_api) + + +def CheckChangeOnCommit(input_api, output_api): + return _RunTests(input_api, output_api) diff --git a/engine/src/flutter/build/README.chromium b/engine/src/flutter/build/README.chromium new file mode 100644 index 0000000000..012df35c7a --- /dev/null +++ b/engine/src/flutter/build/README.chromium @@ -0,0 +1,15 @@ +List of property sheets to be included by projects: + common.vsprops + Not used anymore. No-op. Kept for compatibility with current projects. + + debug.vsprops + Enables debug settings. Must be included directly in Debug configuration. Includes internal\essential.vsprops. + + external_code.vsprops + Contains settings made to simplify usage of external (non-Google) code. It relaxes the warning levels. Should be included after debug.vsprops or release.vsprops to override their settings. + + output_dll_copy.rules + Run to enable automatic copy of DLL when they are as an input file in a vcproj project. + + release.vsprops + Enables release settings. Must be included directly in Release configuration. Includes internal\essential.vsprops. Also includes "internal\release_impl$(CHROME_BUILD_TYPE).vsprops". So the behavior is dependant on the CHROME_BUILD_TYPE environment variable. diff --git a/engine/src/flutter/build/all.gyp b/engine/src/flutter/build/all.gyp new file mode 100644 index 0000000000..6b8ad831a4 --- /dev/null +++ b/engine/src/flutter/build/all.gyp @@ -0,0 +1,1409 @@ +# Copyright (c) 2012 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. + +{ + 'variables': { + # A hook that can be overridden in other repositories to add additional + # compilation targets to 'All'. + 'app_targets%': [], + # For Android-specific targets. + 'android_app_targets%': [], + }, + 'targets': [ + { + 'target_name': 'All', + 'type': 'none', + 'xcode_create_dependents_test_runner': 1, + 'dependencies': [ + '<@(app_targets)', + 'some.gyp:*', + '../base/base.gyp:*', + '../components/components.gyp:*', + '../components/components_tests.gyp:*', + '../content/content.gyp:*', + '../crypto/crypto.gyp:*', + '../net/net.gyp:*', + '../sdch/sdch.gyp:*', + '../sql/sql.gyp:*', + '../testing/gmock.gyp:*', + '../testing/gtest.gyp:*', + '../third_party/icu/icu.gyp:*', + '../third_party/libxml/libxml.gyp:*', + '../third_party/sqlite/sqlite.gyp:*', + '../third_party/zlib/zlib.gyp:*', + '../ui/accessibility/accessibility.gyp:*', + '../ui/base/ui_base.gyp:*', + '../ui/display/display.gyp:display_unittests', + '../ui/snapshot/snapshot.gyp:*', + '../url/url.gyp:*', + ], + 'conditions': [ + ['OS!="ios" and OS!="mac"', { + 'dependencies': [ + '../ui/touch_selection/ui_touch_selection.gyp:*', + ], + }], + ['OS=="ios"', { + 'dependencies': [ + '../chrome/chrome.gyp:browser', + '../chrome/chrome.gyp:browser_ui', + '../ios/ios.gyp:*', + # NOTE: This list of targets is present because + # mojo_base.gyp:mojo_base cannot be built on iOS, as + # javascript-related targets cause v8 to be built. + '../mojo/mojo_base.gyp:mojo_common_lib', + '../mojo/mojo_base.gyp:mojo_common_unittests', + '../google_apis/google_apis.gyp:google_apis_unittests', + '../skia/skia_tests.gyp:skia_unittests', + '../third_party/mojo/mojo_edk.gyp:mojo_system_impl', + '../third_party/mojo/mojo_edk_tests.gyp:mojo_public_bindings_unittests', + '../third_party/mojo/mojo_edk_tests.gyp:mojo_public_environment_unittests', + '../third_party/mojo/mojo_edk_tests.gyp:mojo_public_system_unittests', + '../third_party/mojo/mojo_edk_tests.gyp:mojo_public_utility_unittests', + '../third_party/mojo/mojo_edk_tests.gyp:mojo_system_unittests', + '../third_party/mojo/mojo_public.gyp:mojo_cpp_bindings', + '../third_party/mojo/mojo_public.gyp:mojo_public_test_utils', + '../third_party/mojo/mojo_public.gyp:mojo_system', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../ui/gfx/gfx_tests.gyp:gfx_unittests', + ], + }], + ['OS=="android"', { + 'dependencies': [ + '../content/content_shell_and_tests.gyp:content_shell_apk', + '<@(android_app_targets)', + 'android_builder_tests', + '../tools/telemetry/telemetry.gyp:*#host', + # TODO(nyquist) This should instead by a target for sync when all of + # the sync-related code for Android has been upstreamed. + # See http://crbug.com/159203 + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_javalib', + ], + 'conditions': [ + ['chromecast==0', { + 'dependencies': [ + '../android_webview/android_webview.gyp:android_webview_apk', + '../android_webview/android_webview.gyp:system_webview_apk', + '../android_webview/android_webview_shell.gyp:android_webview_shell_apk', + '../android_webview/android_webview_telemetry_shell.gyp:android_webview_telemetry_shell_apk', + '../chrome/android/chrome_apk.gyp:chrome_public_apk', + '../chrome/chrome.gyp:chrome_shell_apk', + '../chrome/chrome.gyp:chrome_sync_shell_apk', + '../remoting/remoting.gyp:remoting_apk', + ], + }], + ['target_arch == "arm" or target_arch == "arm64"', { + 'dependencies': [ + # The relocation packer is currently used only for ARM or ARM64. + '../third_party/android_platform/relocation_packer.gyp:android_relocation_packer_unittests#host', + ], + }], + ], + }, { + 'dependencies': [ + '../content/content_shell_and_tests.gyp:*', + # TODO: This should build on Android and the target should move to the list above. + '../sync/sync.gyp:*', + ], + }], + ['OS!="ios" and OS!="android" and chromecast==0', { + 'dependencies': [ + '../third_party/re2/re2.gyp:re2', + '../chrome/chrome.gyp:*', + '../chrome/tools/profile_reset/jtl_compiler.gyp:*', + '../cc/blink/cc_blink_tests.gyp:*', + '../cc/cc_tests.gyp:*', + '../device/usb/usb.gyp:*', + '../extensions/extensions.gyp:*', + '../extensions/extensions_tests.gyp:*', + '../gin/gin.gyp:*', + '../gpu/gpu.gyp:*', + '../gpu/tools/tools.gyp:*', + '../ipc/ipc.gyp:*', + '../ipc/mojo/ipc_mojo.gyp:*', + '../jingle/jingle.gyp:*', + '../media/cast/cast.gyp:*', + '../media/media.gyp:*', + '../media/midi/midi.gyp:*', + '../mojo/mojo.gyp:*', + '../mojo/mojo_base.gyp:*', + '../ppapi/ppapi.gyp:*', + '../ppapi/ppapi_internal.gyp:*', + '../ppapi/tools/ppapi_tools.gyp:*', + '../printing/printing.gyp:*', + '../skia/skia.gyp:*', + '../sync/tools/sync_tools.gyp:*', + '../third_party/WebKit/public/all.gyp:*', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:*', + '../third_party/codesighs/codesighs.gyp:*', + '../third_party/ffmpeg/ffmpeg.gyp:*', + '../third_party/iccjpeg/iccjpeg.gyp:*', + '../third_party/libpng/libpng.gyp:*', + '../third_party/libusb/libusb.gyp:*', + '../third_party/libwebp/libwebp.gyp:*', + '../third_party/libxslt/libxslt.gyp:*', + '../third_party/lzma_sdk/lzma_sdk.gyp:*', + '../third_party/mesa/mesa.gyp:*', + '../third_party/modp_b64/modp_b64.gyp:*', + '../third_party/npapi/npapi.gyp:*', + '../third_party/ots/ots.gyp:*', + '../third_party/pdfium/samples/samples.gyp:*', + '../third_party/qcms/qcms.gyp:*', + '../tools/gn/gn.gyp:*', + '../tools/perf/clear_system_cache/clear_system_cache.gyp:*', + '../tools/telemetry/telemetry.gyp:*', + '../v8/tools/gyp/v8.gyp:*', + '<(libjpeg_gyp_path):*', + ], + }], + ['OS!="ios"', { + 'dependencies': [ + '../device/bluetooth/bluetooth.gyp:*', + '../device/device_tests.gyp:*', + ], + }], + ['use_openssl==0 and (OS=="mac" or OS=="ios" or OS=="win")', { + 'dependencies': [ + '../third_party/nss/nss.gyp:*', + ], + }], + ['OS=="win" or OS=="ios" or OS=="linux"', { + 'dependencies': [ + '../breakpad/breakpad.gyp:*', + ], + }], + ['OS=="mac"', { + 'dependencies': [ + '../sandbox/sandbox.gyp:*', + '../third_party/crashpad/crashpad/crashpad.gyp:*', + '../third_party/ocmock/ocmock.gyp:*', + ], + }], + ['OS=="linux"', { + 'dependencies': [ + '../courgette/courgette.gyp:*', + '../sandbox/sandbox.gyp:*', + ], + 'conditions': [ + ['branding=="Chrome"', { + 'dependencies': [ + '../chrome/chrome.gyp:linux_packages_<(channel)', + ], + }], + ['enable_ipc_fuzzer==1', { + 'dependencies': [ + '../tools/ipc_fuzzer/ipc_fuzzer.gyp:*', + ], + }], + ['use_dbus==1', { + 'dependencies': [ + '../dbus/dbus.gyp:*', + ], + }], + ], + }], + ['chromecast==1', { + 'dependencies': [ + '../chromecast/chromecast.gyp:*', + ], + }], + ['use_x11==1', { + 'dependencies': [ + '../tools/xdisplaycheck/xdisplaycheck.gyp:*', + ], + }], + ['OS=="win"', { + 'conditions': [ + ['win_use_allocator_shim==1', { + 'dependencies': [ + '../base/allocator/allocator.gyp:*', + ], + }], + ], + 'dependencies': [ + '../chrome/tools/crash_service/caps/caps.gyp:*', + '../chrome_elf/chrome_elf.gyp:*', + '../cloud_print/cloud_print.gyp:*', + '../courgette/courgette.gyp:*', + '../rlz/rlz.gyp:*', + '../sandbox/sandbox.gyp:*', + '<(angle_path)/src/angle.gyp:*', + '../third_party/bspatch/bspatch.gyp:*', + '../tools/win/static_initializers/static_initializers.gyp:*', + ], + }, { + 'dependencies': [ + '../third_party/libevent/libevent.gyp:*', + ], + }], + ['toolkit_views==1', { + 'dependencies': [ + '../ui/views/controls/webview/webview.gyp:*', + '../ui/views/views.gyp:*', + ], + }], + ['use_aura==1', { + 'dependencies': [ + '../ui/aura/aura.gyp:*', + '../ui/aura_extra/aura_extra.gyp:*', + ], + }], + ['use_ash==1', { + 'dependencies': [ + '../ash/ash.gyp:*', + ], + }], + ['remoting==1', { + 'dependencies': [ + '../remoting/remoting_all.gyp:remoting_all', + ], + }], + ['use_openssl==0', { + 'dependencies': [ + '../net/third_party/nss/ssl.gyp:*', + ], + }], + ['use_openssl==1', { + 'dependencies': [ + '../third_party/boringssl/boringssl.gyp:*', + '../third_party/boringssl/boringssl_tests.gyp:*', + ], + }], + ['enable_app_list==1', { + 'dependencies': [ + '../ui/app_list/app_list.gyp:*', + ], + }], + ['OS!="android" and OS!="ios"', { + 'dependencies': [ + '../google_apis/gcm/gcm.gyp:*', + ], + }], + ['(chromeos==1 or OS=="linux" or OS=="win" or OS=="mac") and chromecast==0', { + 'dependencies': [ + '../extensions/shell/app_shell.gyp:*', + ], + }], + ['envoy==1', { + 'dependencies': [ + '../envoy/envoy.gyp:*', + ], + }], + ], + }, # target_name: All + { + 'target_name': 'All_syzygy', + 'type': 'none', + 'conditions': [ + ['OS=="win" and fastbuild==0 and target_arch=="ia32" and ' + '(syzyasan==1 or syzygy_optimize==1)', { + 'dependencies': [ + '../chrome/installer/mini_installer_syzygy.gyp:*', + ], + }], + ], + }, # target_name: All_syzygy + { + # Note: Android uses android_builder_tests below. + # TODO: Consider merging that with this target. + 'target_name': 'chromium_builder_tests', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base_unittests', + '../components/components_tests.gyp:components_unittests', + '../crypto/crypto.gyp:crypto_unittests', + '../net/net.gyp:net_unittests', + '../skia/skia_tests.gyp:skia_unittests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../ui/display/display.gyp:display_unittests', + '../ui/gfx/gfx_tests.gyp:gfx_unittests', + '../url/url.gyp:url_unittests', + ], + 'conditions': [ + ['OS!="ios"', { + 'dependencies': [ + '../ui/gl/gl_tests.gyp:gl_unittests', + ], + }], + ['OS!="ios" and OS!="mac"', { + 'dependencies': [ + '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests', + ], + }], + ['OS!="ios" and OS!="android"', { + 'dependencies': [ + '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests', + '../cc/cc_tests.gyp:cc_unittests', + '../cloud_print/cloud_print.gyp:cloud_print_unittests', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_shell', + '../content/content_shell_and_tests.gyp:content_unittests', + '../device/device_tests.gyp:device_unittests', + '../gin/gin.gyp:gin_unittests', + '../google_apis/google_apis.gyp:google_apis_unittests', + '../gpu/gles2_conform_support/gles2_conform_support.gyp:gles2_conform_support', + '../gpu/gpu.gyp:gpu_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../ipc/mojo/ipc_mojo.gyp:ipc_mojo_unittests', + '../jingle/jingle.gyp:jingle_unittests', + '../media/cast/cast.gyp:cast_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../mojo/mojo.gyp:mojo', + '../ppapi/ppapi_internal.gyp:ppapi_unittests', + '../remoting/remoting.gyp:remoting_unittests', + '../third_party/WebKit/public/all.gyp:all_blink', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', + '../third_party/leveldatabase/leveldatabase.gyp:env_chromium_unittests', + '../third_party/libaddressinput/libaddressinput.gyp:libaddressinput_unittests', + '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber_unittests', + '../tools/telemetry/telemetry.gyp:*', + ], + }], + ['OS!="ios" and OS!="android" and chromecast==0', { + 'dependencies': [ + '../chrome/chrome.gyp:browser_tests', + '../chrome/chrome.gyp:chromedriver_tests', + '../chrome/chrome.gyp:chromedriver_unittests', + '../chrome/chrome.gyp:interactive_ui_tests', + '../chrome/chrome.gyp:sync_integration_tests', + '../chrome/chrome.gyp:unit_tests', + '../extensions/extensions_tests.gyp:extensions_browsertests', + '../extensions/extensions_tests.gyp:extensions_unittests', + ], + }], + ['OS=="win"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service', + '../chrome/chrome.gyp:installer_util_unittests', + # ../chrome/test/mini_installer requires mini_installer. + '../chrome/installer/mini_installer.gyp:mini_installer', + '../chrome_elf/chrome_elf.gyp:chrome_elf_unittests', + '../content/content_shell_and_tests.gyp:copy_test_netscape_plugin', + '../courgette/courgette.gyp:courgette_unittests', + '../sandbox/sandbox.gyp:sbox_integration_tests', + '../sandbox/sandbox.gyp:sbox_unittests', + '../sandbox/sandbox.gyp:sbox_validation_tests', + '../ui/app_list/app_list.gyp:app_list_unittests', + ], + 'conditions': [ + # remoting_host_installation uses lots of non-trivial GYP that tend + # to break because of differences between ninja and msbuild. Make + # sure this target is built by the builders on the main waterfall. + # See http://crbug.com/180600. + ['wix_exists == "True" and sas_dll_exists == "True"', { + 'dependencies': [ + '../remoting/remoting.gyp:remoting_host_installation', + ], + }], + ['syzyasan==1', { + 'variables': { + # Disable incremental linking for all modules. + # 0: inherit, 1: disabled, 2: enabled. + 'msvs_debug_link_incremental': '1', + 'msvs_large_module_debug_link_mode': '1', + # Disable RTC. Syzygy explicitly doesn't support RTC + # instrumented binaries for now. + 'win_debug_RuntimeChecks': '0', + }, + 'defines': [ + # Disable iterator debugging (huge speed boost). + '_HAS_ITERATOR_DEBUGGING=0', + ], + 'msvs_settings': { + 'VCLinkerTool': { + # Enable profile information (necessary for SyzyAsan + # instrumentation). This is incompatible with incremental + # linking. + 'Profile': 'true', + }, + } + }], + ], + }], + ['chromeos==1', { + 'dependencies': [ + '../ui/chromeos/ui_chromeos.gyp:ui_chromeos_unittests', + ], + }], + ['OS=="linux"', { + 'dependencies': [ + '../sandbox/sandbox.gyp:sandbox_linux_unittests', + ], + }], + ['OS=="linux" and use_dbus==1', { + 'dependencies': [ + '../dbus/dbus.gyp:dbus_unittests', + ], + }], + ['OS=="mac"', { + 'dependencies': [ + '../ui/app_list/app_list.gyp:app_list_unittests', + '../ui/message_center/message_center.gyp:*', + ], + }], + ['test_isolation_mode != "noop"', { + 'dependencies': [ + 'chromium_swarm_tests', + ], + }], + ['OS!="android"', { + 'dependencies': [ + '../google_apis/gcm/gcm.gyp:gcm_unit_tests', + ], + }], + ['enable_basic_printing==1 or enable_print_preview==1', { + 'dependencies': [ + '../printing/printing.gyp:printing_unittests', + ], + }], + ['use_aura==1', { + 'dependencies': [ + '../ui/app_list/app_list.gyp:app_list_unittests', + '../ui/aura/aura.gyp:aura_unittests', + '../ui/compositor/compositor.gyp:compositor_unittests', + ], + }], + ['use_aura==1 and chromecast==0', { + 'dependencies': [ + '../ui/keyboard/keyboard.gyp:keyboard_unittests', + '../ui/views/views.gyp:views_unittests', + ], + }], + ['use_aura==1 or toolkit_views==1', { + 'dependencies': [ + '../ui/events/events.gyp:events_unittests', + ], + }], + ['use_ash==1', { + 'dependencies': [ + '../ash/ash.gyp:ash_unittests', + ], + }], + ['disable_nacl==0', { + 'dependencies': [ + '../components/nacl.gyp:nacl_loader_unittests', + ], + }], + ['disable_nacl==0 and disable_nacl_untrusted==0 and enable_nacl_nonsfi_test==1', { + 'dependencies': [ + '../components/nacl.gyp:nacl_helper_nonsfi_unittests', + ], + }], + ['disable_nacl==0 and disable_nacl_untrusted==0', { + 'dependencies': [ + '../mojo/mojo_nacl_untrusted.gyp:libmojo', + '../mojo/mojo_nacl.gyp:monacl_codegen', + '../mojo/mojo_nacl.gyp:monacl_sel', + '../mojo/mojo_nacl.gyp:monacl_shell', + ], + }], + ], + }, # target_name: chromium_builder_tests + ], + 'conditions': [ + # TODO(GYP): make gn_migration.gypi work unconditionally. + ['OS=="mac" or OS=="win" or (OS=="linux" and target_arch=="x64" and chromecast==0)', { + 'includes': [ + 'gn_migration.gypi', + ], + }], + ['OS!="ios"', { + 'targets': [ + { + 'target_name': 'blink_tests', + 'type': 'none', + 'dependencies': [ + '../third_party/WebKit/public/all.gyp:all_blink', + ], + 'conditions': [ + ['OS=="android"', { + 'dependencies': [ + '../content/content_shell_and_tests.gyp:content_shell_apk', + '../breakpad/breakpad.gyp:dump_syms#host', + '../breakpad/breakpad.gyp:minidump_stackwalk#host', + ], + }, { # OS!="android" + 'dependencies': [ + '../content/content_shell_and_tests.gyp:content_shell', + ], + }], + ['OS=="win"', { + 'dependencies': [ + '../components/test_runner/test_runner.gyp:layout_test_helper', + '../content/content_shell_and_tests.gyp:content_shell_crash_service', + ], + }], + ['OS!="win" and OS!="android"', { + 'dependencies': [ + '../breakpad/breakpad.gyp:minidump_stackwalk', + ], + }], + ['OS=="mac"', { + 'dependencies': [ + '../components/test_runner/test_runner.gyp:layout_test_helper', + '../breakpad/breakpad.gyp:dump_syms#host', + ], + }], + ['OS=="linux"', { + 'dependencies': [ + '../breakpad/breakpad.gyp:dump_syms#host', + ], + }], + ], + }, # target_name: blink_tests + ], + }], # OS!=ios + ['OS!="ios" and OS!="android" and chromecast==0', { + 'targets': [ + { + 'target_name': 'chromium_builder_nacl_win_integration', + 'type': 'none', + 'dependencies': [ + 'chromium_builder_tests', + ], + }, # target_name: chromium_builder_nacl_win_integration + { + 'target_name': 'chromium_builder_perf', + 'type': 'none', + 'dependencies': [ + '../cc/cc_tests.gyp:cc_perftests', + '../chrome/chrome.gyp:chrome', + '../chrome/chrome.gyp:load_library_perf_tests', + '../chrome/chrome.gyp:performance_browser_tests', + '../chrome/chrome.gyp:sync_performance_tests', + '../content/content_shell_and_tests.gyp:content_shell', + '../gpu/gpu.gyp:gpu_perftests', + '../media/media.gyp:media_perftests', + '../media/midi/midi.gyp:midi_unittests', + '../tools/perf/clear_system_cache/clear_system_cache.gyp:*', + '../tools/telemetry/telemetry.gyp:*', + ], + 'conditions': [ + ['OS!="ios" and OS!="win"', { + 'dependencies': [ + '../breakpad/breakpad.gyp:minidump_stackwalk', + ], + }], + ['OS=="linux"', { + 'dependencies': [ + '../chrome/chrome.gyp:linux_symbols' + ], + }], + ['OS=="win"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service', + '../gpu/gpu.gyp:angle_perftests', + ], + }], + ['OS=="win" and target_arch=="ia32"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service_win64', + ], + }], + ], + }, # target_name: chromium_builder_perf + { + 'target_name': 'chromium_gpu_builder', + 'type': 'none', + 'dependencies': [ + '../chrome/chrome.gyp:chrome', + '../chrome/chrome.gyp:performance_browser_tests', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_gl_tests', + '../gpu/gles2_conform_support/gles2_conform_test.gyp:gles2_conform_test', + '../gpu/khronos_glcts_support/khronos_glcts_test.gyp:khronos_glcts_test', + '../gpu/gpu.gyp:gl_tests', + '../gpu/gpu.gyp:angle_unittests', + '../gpu/gpu.gyp:gpu_unittests', + '../tools/telemetry/telemetry.gyp:*', + ], + 'conditions': [ + ['OS!="ios" and OS!="win"', { + 'dependencies': [ + '../breakpad/breakpad.gyp:minidump_stackwalk', + ], + }], + ['OS=="linux"', { + 'dependencies': [ + '../chrome/chrome.gyp:linux_symbols' + ], + }], + ['OS=="win"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service', + ], + }], + ['OS=="win" and target_arch=="ia32"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service_win64', + ], + }], + ], + }, # target_name: chromium_gpu_builder + { + 'target_name': 'chromium_gpu_debug_builder', + 'type': 'none', + 'dependencies': [ + '../chrome/chrome.gyp:chrome', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_gl_tests', + '../gpu/gles2_conform_support/gles2_conform_test.gyp:gles2_conform_test', + '../gpu/khronos_glcts_support/khronos_glcts_test.gyp:khronos_glcts_test', + '../gpu/gpu.gyp:gl_tests', + '../gpu/gpu.gyp:angle_unittests', + '../gpu/gpu.gyp:gpu_unittests', + '../tools/telemetry/telemetry.gyp:*', + ], + 'conditions': [ + ['OS!="ios" and OS!="win"', { + 'dependencies': [ + '../breakpad/breakpad.gyp:minidump_stackwalk', + ], + }], + ['OS=="linux"', { + 'dependencies': [ + '../chrome/chrome.gyp:linux_symbols' + ], + }], + ['OS=="win"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service', + ], + }], + ['OS=="win" and target_arch=="ia32"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service_win64', + ], + }], + ], + }, # target_name: chromium_gpu_debug_builder + { + # This target contains everything we need to run tests on the special + # device-equipped WebRTC bots. We have device-requiring tests in + # browser_tests and content_browsertests. + 'target_name': 'chromium_builder_webrtc', + 'type': 'none', + 'dependencies': [ + 'chromium_builder_perf', + '../chrome/chrome.gyp:browser_tests', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../third_party/webrtc/tools/tools.gyp:frame_analyzer', + '../third_party/webrtc/tools/tools.gyp:rgba_to_i420_converter', + ], + 'conditions': [ + ['remoting==1', { + 'dependencies': [ + '../remoting/remoting.gyp:*', + ], + }], + ], + }, # target_name: chromium_builder_webrtc + { + 'target_name': 'chromium_builder_chromedriver', + 'type': 'none', + 'dependencies': [ + '../chrome/chrome.gyp:chromedriver', + '../chrome/chrome.gyp:chromedriver_tests', + '../chrome/chrome.gyp:chromedriver_unittests', + ], + }, # target_name: chromium_builder_chromedriver + { + 'target_name': 'chromium_builder_asan', + 'type': 'none', + 'dependencies': [ + '../chrome/chrome.gyp:chrome', + + # We refer to content_shell directly rather than blink_tests + # because we don't want the _unittests binaries. + '../content/content_shell_and_tests.gyp:content_shell', + ], + 'conditions': [ + ['OS!="win"', { + 'dependencies': [ + '../net/net.gyp:hpack_fuzz_wrapper', + '../net/net.gyp:dns_fuzz_stub', + '../skia/skia.gyp:filter_fuzz_stub', + ], + }], + ['enable_ipc_fuzzer==1 and component!="shared_library" and ' + '(OS=="linux" or OS=="win")', { + 'dependencies': [ + '../tools/ipc_fuzzer/ipc_fuzzer.gyp:*', + ], + }], + ['chromeos==0', { + 'dependencies': [ + '../v8/src/d8.gyp:d8#host', + '../third_party/pdfium/samples/samples.gyp:pdfium_test', + ], + }], + ['internal_filter_fuzzer==1', { + 'dependencies': [ + '../skia/tools/clusterfuzz-data/fuzzers/filter_fuzzer/filter_fuzzer.gyp:filter_fuzzer', + ], + }], # internal_filter_fuzzer + ['clang==1', { + 'dependencies': [ + 'sanitizers/sanitizers.gyp:llvm-symbolizer', + ], + }], + ['OS=="win" and fastbuild==0 and target_arch=="ia32" and syzyasan==1', { + 'dependencies': [ + '../chrome/chrome_syzygy.gyp:chrome_dll_syzygy', + '../content/content_shell_and_tests.gyp:content_shell_syzyasan', + ], + 'conditions': [ + ['chrome_multiple_dll==1', { + 'dependencies': [ + '../chrome/chrome_syzygy.gyp:chrome_child_dll_syzygy', + ], + }], + ], + }], + ], + }, + { + 'target_name': 'chromium_builder_nacl_sdk', + 'type': 'none', + 'dependencies': [ + '../chrome/chrome.gyp:chrome', + ], + 'conditions': [ + ['OS=="win"', { + 'dependencies': [ + '../chrome/chrome.gyp:chrome_nacl_win64', + ] + }], + ], + }, #target_name: chromium_builder_nacl_sdk + ], # targets + }], #OS!=ios and OS!=android + ['OS=="android"', { + 'targets': [ + { + # The current list of tests for android. This is temporary + # until the full set supported. If adding a new test here, + # please also add it to build/android/pylib/gtest/gtest_config.py, + # else the test is not run. + # + # WARNING: + # Do not add targets here without communicating the implications + # on tryserver triggers and load. Discuss with + # chrome-infrastructure-team please. + 'target_name': 'android_builder_tests', + 'type': 'none', + 'dependencies': [ + '../base/android/jni_generator/jni_generator.gyp:jni_generator_tests', + '../base/base.gyp:base_unittests', + '../breakpad/breakpad.gyp:breakpad_unittests_deps', + # Also compile the tools needed to deal with minidumps, they are + # needed to run minidump tests upstream. + '../breakpad/breakpad.gyp:dump_syms#host', + '../breakpad/breakpad.gyp:symupload#host', + '../breakpad/breakpad.gyp:minidump_dump#host', + '../breakpad/breakpad.gyp:minidump_stackwalk#host', + '../build/android/tests/multiple_proguards/multiple_proguards.gyp:multiple_proguards_test_apk', + '../build/android/pylib/device/commands/commands.gyp:chromium_commands', + '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests', + '../cc/cc_tests.gyp:cc_perftests_apk', + '../cc/cc_tests.gyp:cc_unittests', + '../components/components_tests.gyp:components_unittests', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_gl_tests', + '../content/content_shell_and_tests.gyp:content_junit_tests', + '../content/content_shell_and_tests.gyp:chromium_linker_test_apk', + '../content/content_shell_and_tests.gyp:content_shell_test_apk', + '../content/content_shell_and_tests.gyp:content_unittests', + '../gpu/gpu.gyp:gl_tests', + '../gpu/gpu.gyp:gpu_perftests_apk', + '../gpu/gpu.gyp:gpu_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../media/media.gyp:media_perftests_apk', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests_apk', + '../media/midi/midi.gyp:midi_unittests', + '../net/net.gyp:net_unittests', + '../sandbox/sandbox.gyp:sandbox_linux_unittests_deps', + '../skia/skia_tests.gyp:skia_unittests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + '../testing/android/junit/junit_test.gyp:junit_unit_tests', + '../third_party/leveldatabase/leveldatabase.gyp:env_chromium_unittests', + '../third_party/WebKit/public/all.gyp:*', + '../tools/android/android_tools.gyp:android_tools', + '../tools/android/android_tools.gyp:memconsumer', + '../tools/android/findbugs_plugin/findbugs_plugin.gyp:findbugs_plugin_test', + '../ui/android/ui_android.gyp:ui_android_unittests', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../ui/events/events.gyp:events_unittests', + '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests', + # Unit test bundles packaged as an apk. + '../base/base.gyp:base_unittests_apk', + '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests_apk', + '../cc/cc_tests.gyp:cc_unittests_apk', + '../components/components_tests.gyp:components_browsertests_apk', + '../components/components_tests.gyp:components_unittests_apk', + '../content/content_shell_and_tests.gyp:content_browsertests_apk', + '../content/content_shell_and_tests.gyp:content_gl_tests_apk', + '../content/content_shell_and_tests.gyp:content_unittests_apk', + '../content/content_shell_and_tests.gyp:video_decode_accelerator_unittest_apk', + '../gpu/gpu.gyp:gl_tests_apk', + '../gpu/gpu.gyp:gpu_unittests_apk', + '../ipc/ipc.gyp:ipc_tests_apk', + '../media/media.gyp:media_unittests_apk', + '../media/midi/midi.gyp:midi_unittests_apk', + '../net/net.gyp:net_unittests_apk', + '../sandbox/sandbox.gyp:sandbox_linux_jni_unittests_apk', + '../skia/skia_tests.gyp:skia_unittests_apk', + '../sql/sql.gyp:sql_unittests_apk', + '../sync/sync.gyp:sync_unit_tests_apk', + '../tools/android/heap_profiler/heap_profiler.gyp:heap_profiler_unittests_apk', + '../ui/android/ui_android.gyp:ui_android_unittests_apk', + '../ui/base/ui_base_tests.gyp:ui_base_unittests_apk', + '../ui/events/events.gyp:events_unittests_apk', + '../ui/gfx/gfx_tests.gyp:gfx_unittests_apk', + '../ui/gl/gl_tests.gyp:gl_unittests_apk', + '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests_apk', + ], + 'conditions': [ + ['chromecast==0', { + 'dependencies': [ + '../android_webview/android_webview.gyp:android_webview_unittests', + '../chrome/chrome.gyp:unit_tests', + # Unit test bundles packaged as an apk. + '../android_webview/android_webview.gyp:android_webview_test_apk', + '../android_webview/android_webview.gyp:android_webview_unittests_apk', + '../chrome/android/chrome_apk.gyp:chrome_public_test_apk', + '../chrome/chrome.gyp:chrome_junit_tests', + '../chrome/chrome.gyp:chrome_shell_test_apk', + '../chrome/chrome.gyp:chrome_sync_shell_test_apk', + '../chrome/chrome.gyp:chrome_shell_uiautomator_tests', + '../chrome/chrome.gyp:chromedriver_webview_shell_apk', + '../chrome/chrome.gyp:unit_tests_apk', + ], + }], + ], + }, + { + # WebRTC Chromium tests to run on Android. + 'target_name': 'android_builder_chromium_webrtc', + 'type': 'none', + 'dependencies': [ + '../build/android/pylib/device/commands/commands.gyp:chromium_commands', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../tools/android/android_tools.gyp:android_tools', + '../tools/android/android_tools.gyp:memconsumer', + '../content/content_shell_and_tests.gyp:content_browsertests_apk', + ], + }, # target_name: android_builder_chromium_webrtc + ], # targets + }], # OS="android" + ['OS=="mac"', { + 'targets': [ + { + # Target to build everything plus the dmg. We don't put the dmg + # in the All target because developers really don't need it. + 'target_name': 'all_and_dmg', + 'type': 'none', + 'dependencies': [ + 'All', + '../chrome/chrome.gyp:build_app_dmg', + ], + }, + # These targets are here so the build bots can use them to build + # subsets of a full tree for faster cycle times. + { + 'target_name': 'chromium_builder_dbg', + 'type': 'none', + 'dependencies': [ + '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests', + '../cc/cc_tests.gyp:cc_unittests', + '../chrome/chrome.gyp:browser_tests', + '../chrome/chrome.gyp:interactive_ui_tests', + '../chrome/chrome.gyp:sync_integration_tests', + '../chrome/chrome.gyp:unit_tests', + '../cloud_print/cloud_print.gyp:cloud_print_unittests', + '../components/components_tests.gyp:components_unittests', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_unittests', + '../device/device_tests.gyp:device_unittests', + '../google_apis/gcm/gcm.gyp:gcm_unit_tests', + '../gpu/gpu.gyp:gpu_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../ipc/mojo/ipc_mojo.gyp:ipc_mojo_unittests', + '../jingle/jingle.gyp:jingle_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../ppapi/ppapi_internal.gyp:ppapi_unittests', + '../printing/printing.gyp:printing_unittests', + '../remoting/remoting.gyp:remoting_unittests', + '../rlz/rlz.gyp:*', + '../skia/skia_tests.gyp:skia_unittests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', + '../third_party/leveldatabase/leveldatabase.gyp:env_chromium_unittests', + '../third_party/libaddressinput/libaddressinput.gyp:libaddressinput_unittests', + '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber_unittests', + '../tools/perf/clear_system_cache/clear_system_cache.gyp:*', + '../tools/telemetry/telemetry.gyp:*', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../ui/gfx/gfx_tests.gyp:gfx_unittests', + '../ui/gl/gl_tests.gyp:gl_unittests', + '../url/url.gyp:url_unittests', + ], + }, + { + 'target_name': 'chromium_builder_rel', + 'type': 'none', + 'dependencies': [ + '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests', + '../cc/cc_tests.gyp:cc_unittests', + '../chrome/chrome.gyp:browser_tests', + '../chrome/chrome.gyp:performance_browser_tests', + '../chrome/chrome.gyp:sync_integration_tests', + '../chrome/chrome.gyp:unit_tests', + '../cloud_print/cloud_print.gyp:cloud_print_unittests', + '../components/components_tests.gyp:components_unittests', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_unittests', + '../device/device_tests.gyp:device_unittests', + '../google_apis/gcm/gcm.gyp:gcm_unit_tests', + '../gpu/gpu.gyp:gpu_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../ipc/mojo/ipc_mojo.gyp:ipc_mojo_unittests', + '../jingle/jingle.gyp:jingle_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../ppapi/ppapi_internal.gyp:ppapi_unittests', + '../printing/printing.gyp:printing_unittests', + '../remoting/remoting.gyp:remoting_unittests', + '../skia/skia_tests.gyp:skia_unittests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', + '../third_party/leveldatabase/leveldatabase.gyp:env_chromium_unittests', + '../third_party/libaddressinput/libaddressinput.gyp:libaddressinput_unittests', + '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber_unittests', + '../tools/perf/clear_system_cache/clear_system_cache.gyp:*', + '../tools/telemetry/telemetry.gyp:*', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../ui/gfx/gfx_tests.gyp:gfx_unittests', + '../ui/gl/gl_tests.gyp:gl_unittests', + '../url/url.gyp:url_unittests', + ], + }, + { + 'target_name': 'chromium_builder_dbg_tsan_mac', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base_unittests', + '../cloud_print/cloud_print.gyp:cloud_print_unittests', + '../crypto/crypto.gyp:crypto_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../jingle/jingle.gyp:jingle_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../net/net.gyp:net_unittests', + '../printing/printing.gyp:printing_unittests', + '../remoting/remoting.gyp:remoting_unittests', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', + '../third_party/libaddressinput/libaddressinput.gyp:libaddressinput_unittests', + '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber_unittests', + '../url/url.gyp:url_unittests', + ], + }, + { + 'target_name': 'chromium_builder_dbg_valgrind_mac', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base_unittests', + '../chrome/chrome.gyp:unit_tests', + '../cloud_print/cloud_print.gyp:cloud_print_unittests', + '../components/components_tests.gyp:components_unittests', + '../content/content_shell_and_tests.gyp:content_unittests', + '../crypto/crypto.gyp:crypto_unittests', + '../device/device_tests.gyp:device_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../jingle/jingle.gyp:jingle_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../net/net.gyp:net_unittests', + '../google_apis/gcm/gcm.gyp:gcm_unit_tests', + '../printing/printing.gyp:printing_unittests', + '../remoting/remoting.gyp:remoting_unittests', + '../skia/skia_tests.gyp:skia_unittests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', + '../third_party/leveldatabase/leveldatabase.gyp:env_chromium_unittests', + '../third_party/libaddressinput/libaddressinput.gyp:libaddressinput_unittests', + '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber_unittests', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../ui/gfx/gfx_tests.gyp:gfx_unittests', + '../ui/gl/gl_tests.gyp:gl_unittests', + '../url/url.gyp:url_unittests', + ], + }, + ], # targets + }], # OS="mac" + ['OS=="win"', { + 'targets': [ + # These targets are here so the build bots can use them to build + # subsets of a full tree for faster cycle times. + { + 'target_name': 'chromium_builder', + 'type': 'none', + 'dependencies': [ + '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests', + '../cc/cc_tests.gyp:cc_unittests', + '../chrome/chrome.gyp:browser_tests', + '../chrome/chrome.gyp:crash_service', + '../chrome/chrome.gyp:gcapi_test', + '../chrome/chrome.gyp:installer_util_unittests', + '../chrome/chrome.gyp:interactive_ui_tests', + '../chrome/chrome.gyp:performance_browser_tests', + '../chrome/chrome.gyp:sync_integration_tests', + '../chrome/chrome.gyp:unit_tests', + '../cloud_print/cloud_print.gyp:cloud_print_unittests', + '../components/components_tests.gyp:components_unittests', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_unittests', + '../content/content_shell_and_tests.gyp:copy_test_netscape_plugin', + # ../chrome/test/mini_installer requires mini_installer. + '../chrome/installer/mini_installer.gyp:mini_installer', + '../courgette/courgette.gyp:courgette_unittests', + '../device/device_tests.gyp:device_unittests', + '../google_apis/gcm/gcm.gyp:gcm_unit_tests', + '../gpu/gpu.gyp:gpu_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../ipc/mojo/ipc_mojo.gyp:ipc_mojo_unittests', + '../jingle/jingle.gyp:jingle_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../ppapi/ppapi_internal.gyp:ppapi_unittests', + '../printing/printing.gyp:printing_unittests', + '../remoting/remoting.gyp:remoting_unittests', + '../skia/skia_tests.gyp:skia_unittests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', + '../third_party/leveldatabase/leveldatabase.gyp:env_chromium_unittests', + '../third_party/libaddressinput/libaddressinput.gyp:libaddressinput_unittests', + '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber_unittests', + '../tools/perf/clear_system_cache/clear_system_cache.gyp:*', + '../tools/telemetry/telemetry.gyp:*', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../ui/events/events.gyp:events_unittests', + '../ui/gfx/gfx_tests.gyp:gfx_unittests', + '../ui/gl/gl_tests.gyp:gl_unittests', + '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests', + '../ui/views/views.gyp:views_unittests', + '../url/url.gyp:url_unittests', + ], + 'conditions': [ + ['target_arch=="ia32"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service_win64', + ], + }], + ], + }, + { + 'target_name': 'chromium_builder_dbg_tsan_win', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base_unittests', + '../cloud_print/cloud_print.gyp:cloud_print_unittests', + '../components/components_tests.gyp:components_unittests', + '../content/content_shell_and_tests.gyp:content_unittests', + '../crypto/crypto.gyp:crypto_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../jingle/jingle.gyp:jingle_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../net/net.gyp:net_unittests', + '../printing/printing.gyp:printing_unittests', + '../remoting/remoting.gyp:remoting_unittests', + '../sql/sql.gyp:sql_unittests', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', + '../third_party/leveldatabase/leveldatabase.gyp:env_chromium_unittests', + '../third_party/libaddressinput/libaddressinput.gyp:libaddressinput_unittests', + '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber_unittests', + '../url/url.gyp:url_unittests', + ], + }, + { + 'target_name': 'chromium_builder_lkgr_drmemory_win', + 'type': 'none', + 'dependencies': [ + '../components/test_runner/test_runner.gyp:layout_test_helper', + '../content/content_shell_and_tests.gyp:content_shell', + '../content/content_shell_and_tests.gyp:content_shell_crash_service', + ], + }, + { + 'target_name': 'chromium_builder_dbg_drmemory_win', + 'type': 'none', + 'dependencies': [ + '../ash/ash.gyp:ash_shell_unittests', + '../ash/ash.gyp:ash_unittests', + '../base/base.gyp:base_unittests', + '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests', + '../cc/cc_tests.gyp:cc_unittests', + '../chrome/chrome.gyp:browser_tests', + '../chrome/chrome.gyp:chrome_app_unittests', + '../chrome/chrome.gyp:chromedriver_unittests', + '../chrome/chrome.gyp:installer_util_unittests', + '../chrome/chrome.gyp:unit_tests', + '../chrome_elf/chrome_elf.gyp:chrome_elf_unittests', + '../cloud_print/cloud_print.gyp:cloud_print_unittests', + '../components/components_tests.gyp:components_unittests', + '../components/test_runner/test_runner.gyp:layout_test_helper', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_shell', + '../content/content_shell_and_tests.gyp:content_shell_crash_service', + '../content/content_shell_and_tests.gyp:content_unittests', + '../courgette/courgette.gyp:courgette_unittests', + '../crypto/crypto.gyp:crypto_unittests', + '../device/device_tests.gyp:device_unittests', + '../extensions/extensions_tests.gyp:extensions_browsertests', + '../extensions/extensions_tests.gyp:extensions_unittests', + '../gin/gin.gyp:gin_shell', + '../gin/gin.gyp:gin_unittests', + '../google_apis/gcm/gcm.gyp:gcm_unit_tests', + '../google_apis/google_apis.gyp:google_apis_unittests', + '../gpu/gpu.gyp:angle_unittests', + '../gpu/gpu.gyp:gpu_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../ipc/mojo/ipc_mojo.gyp:ipc_mojo_unittests', + '../jingle/jingle.gyp:jingle_unittests', + '../media/cast/cast.gyp:cast_unittests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../mojo/mojo.gyp:mojo', + '../net/net.gyp:net_unittests', + '../printing/printing.gyp:printing_unittests', + '../remoting/remoting.gyp:remoting_unittests', + '../skia/skia_tests.gyp:skia_unittests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_unittests', + '../third_party/leveldatabase/leveldatabase.gyp:env_chromium_unittests', + '../third_party/libaddressinput/libaddressinput.gyp:libaddressinput_unittests', + '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber_unittests', + '../third_party/WebKit/Source/platform/blink_platform_tests.gyp:blink_heap_unittests', + '../third_party/WebKit/Source/platform/blink_platform_tests.gyp:blink_platform_unittests', + '../ui/accessibility/accessibility.gyp:accessibility_unittests', + '../ui/app_list/app_list.gyp:app_list_unittests', + '../ui/aura/aura.gyp:aura_unittests', + '../ui/compositor/compositor.gyp:compositor_unittests', + '../ui/display/display.gyp:display_unittests', + '../ui/events/events.gyp:events_unittests', + '../ui/gfx/gfx_tests.gyp:gfx_unittests', + '../ui/gl/gl_tests.gyp:gl_unittests', + '../ui/keyboard/keyboard.gyp:keyboard_unittests', + '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests', + '../url/url.gyp:url_unittests', + ], + }, + ], # targets + 'conditions': [ + ['branding=="Chrome"', { + 'targets': [ + { + 'target_name': 'chrome_official_builder_no_unittests', + 'type': 'none', + 'dependencies': [ + '../chrome/chrome.gyp:crash_service', + '../chrome/chrome.gyp:gcapi_dll', + '../chrome/chrome.gyp:pack_policy_templates', + '../chrome/installer/mini_installer.gyp:mini_installer', + '../cloud_print/cloud_print.gyp:cloud_print', + '../courgette/courgette.gyp:courgette', + '../courgette/courgette.gyp:courgette64', + '../remoting/remoting.gyp:remoting_webapp', + '../third_party/widevine/cdm/widevine_cdm.gyp:widevinecdmadapter', + ], + 'conditions': [ + ['target_arch=="ia32"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service_win64', + ], + }], + ['component != "shared_library" and wix_exists == "True" and \ + sas_dll_exists == "True"', { + 'dependencies': [ + '../remoting/remoting.gyp:remoting_host_installation', + ], + }], # component != "shared_library" + ] + }, { + 'target_name': 'chrome_official_builder', + 'type': 'none', + 'dependencies': [ + 'chrome_official_builder_no_unittests', + '../base/base.gyp:base_unittests', + '../chrome/chrome.gyp:browser_tests', + '../chrome/chrome.gyp:sync_integration_tests', + '../ipc/ipc.gyp:ipc_tests', + '../media/media.gyp:media_unittests', + '../media/midi/midi.gyp:midi_unittests', + '../net/net.gyp:net_unittests', + '../printing/printing.gyp:printing_unittests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../ui/gfx/gfx_tests.gyp:gfx_unittests', + '../ui/gl/gl_tests.gyp:gl_unittests', + '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests', + '../ui/views/views.gyp:views_unittests', + '../url/url.gyp:url_unittests', + ], + }, + ], # targets + }], # branding=="Chrome" + ], # conditions + }], # OS="win" + ['use_aura==1', { + 'targets': [ + { + 'target_name': 'aura_builder', + 'type': 'none', + 'dependencies': [ + '../cc/blink/cc_blink_tests.gyp:cc_blink_unittests', + '../cc/cc_tests.gyp:cc_unittests', + '../components/components_tests.gyp:components_unittests', + '../content/content_shell_and_tests.gyp:content_browsertests', + '../content/content_shell_and_tests.gyp:content_unittests', + '../device/device_tests.gyp:device_unittests', + '../google_apis/gcm/gcm.gyp:gcm_unit_tests', + '../ppapi/ppapi_internal.gyp:ppapi_unittests', + '../remoting/remoting.gyp:remoting_unittests', + '../skia/skia_tests.gyp:skia_unittests', + '../ui/app_list/app_list.gyp:*', + '../ui/aura/aura.gyp:*', + '../ui/aura_extra/aura_extra.gyp:*', + '../ui/base/ui_base_tests.gyp:ui_base_unittests', + '../ui/compositor/compositor.gyp:*', + '../ui/display/display.gyp:display_unittests', + '../ui/events/events.gyp:*', + '../ui/gfx/gfx_tests.gyp:gfx_unittests', + '../ui/gl/gl_tests.gyp:gl_unittests', + '../ui/keyboard/keyboard.gyp:*', + '../ui/snapshot/snapshot.gyp:snapshot_unittests', + '../ui/touch_selection/ui_touch_selection.gyp:ui_touch_selection_unittests', + '../ui/wm/wm.gyp:*', + 'blink_tests', + ], + 'conditions': [ + ['OS=="win"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service', + ], + }], + ['OS=="win" and target_arch=="ia32"', { + 'dependencies': [ + '../chrome/chrome.gyp:crash_service_win64', + ], + }], + ['use_ash==1', { + 'dependencies': [ + '../ash/ash.gyp:ash_shell', + '../ash/ash.gyp:ash_unittests', + ], + }], + ['OS=="linux"', { + # Tests that currently only work on Linux. + 'dependencies': [ + '../base/base.gyp:base_unittests', + '../ipc/ipc.gyp:ipc_tests', + '../sql/sql.gyp:sql_unittests', + '../sync/sync.gyp:sync_unit_tests', + ], + }], + ['chromeos==1', { + 'dependencies': [ + '../chromeos/chromeos.gyp:chromeos_unittests', + '../ui/chromeos/ui_chromeos.gyp:ui_chromeos_unittests', + ], + }], + ['use_ozone==1', { + 'dependencies': [ + '../ui/ozone/ozone.gyp:*', + '../ui/ozone/demo/ozone_demos.gyp:*', + ], + }], + ['chromecast==0', { + 'dependencies': [ + '../chrome/chrome.gyp:browser_tests', + '../chrome/chrome.gyp:chrome', + '../chrome/chrome.gyp:interactive_ui_tests', + '../chrome/chrome.gyp:unit_tests', + '../ui/message_center/message_center.gyp:*', + '../ui/views/examples/examples.gyp:views_examples_with_content_exe', + '../ui/views/views.gyp:views', + '../ui/views/views.gyp:views_unittests', + ], + }], + ], + }, + ], # targets + }], # "use_aura==1" + ['test_isolation_mode != "noop"', { + 'targets': [ + { + 'target_name': 'chromium_swarm_tests', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base_unittests_run', + '../content/content_shell_and_tests.gyp:content_browsertests_run', + '../content/content_shell_and_tests.gyp:content_unittests_run', + '../net/net.gyp:net_unittests_run', + ], + 'conditions': [ + ['chromecast==0', { + 'dependencies': [ + '../chrome/chrome.gyp:browser_tests_run', + '../chrome/chrome.gyp:interactive_ui_tests_run', + '../chrome/chrome.gyp:sync_integration_tests_run', + '../chrome/chrome.gyp:unit_tests_run', + ], + }], + ], + }, # target_name: chromium_swarm_tests + ], + }], + ['archive_chromoting_tests==1', { + 'targets': [ + { + 'target_name': 'chromoting_swarm_tests', + 'type': 'none', + 'dependencies': [ + '../testing/chromoting/integration_tests.gyp:chromoting_integration_tests_run', + ], + }, # target_name: chromoting_swarm_tests + ] + }], + ['OS=="mac" and toolkit_views==1', { + 'targets': [ + { + 'target_name': 'macviews_builder', + 'type': 'none', + 'dependencies': [ + '../ui/views/examples/examples.gyp:views_examples_with_content_exe', + '../ui/views/views.gyp:views', + '../ui/views/views.gyp:views_unittests', + ], + }, # target_name: macviews_builder + ], # targets + }], # os=='mac' and toolkit_views==1 + ], # conditions +} diff --git a/engine/src/flutter/build/android/AndroidManifest.xml b/engine/src/flutter/build/android/AndroidManifest.xml new file mode 100644 index 0000000000..f27872eee4 --- /dev/null +++ b/engine/src/flutter/build/android/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/engine/src/flutter/build/android/CheckInstallApk-debug.apk b/engine/src/flutter/build/android/CheckInstallApk-debug.apk new file mode 100644 index 0000000000000000000000000000000000000000..3dc31910a5388024da4558c9e5f249a37e3783bd GIT binary patch literal 37106 zcmWIWW@Zs#;9%fjU=CaE^is0qtR*7@!wx0}24MykhN9GB{hY+g{L&Kr+{Da0y^7qN zf2SB2S|@JwJM19f^1MJtT4;%gNQ1@-K`$#VD;2ITg;^Y4LXIvvJ+e33TKdjEVqT%~ zLm(@I`MuvZ12Lg;srT>RyvxXct9j*fSIU;ftrM?(DelYuXJ*0RY``R0!J7X-R>v_~ zvt~)Bkf>BMhnnK$K%YMzCH&PZEoWx$(cJmfEzhfdiFA(Rwxi{XT0b+t68dG7mAik- z^Hp4DOIFyKU!VGV|6-%?^?~=5XHR;4_dxMBzU@ne_sp1`6)v79ZkYAkWqC>TT4l8j(UlCWRhK4nZtbj{Xej?cc#dRp%C5fW z*I#sBKJ?41P5x>{HPav7b>V*RreAyJv@+Z4+!c%Lzq9;~hW>K&wg2P%-+pcC$(!>+ zw;NVot9rWo7VqD#_a^sJrE0de=LdMRb0CK?YUt-Uv|bivVqoxLWnd6yU|?{}ODW3F zOz}<3%S=lxE&+vp>*TX}mmNe}&)>*=Www8c!oo|e++iLP6CxDCrf_ivykP3IeJ8)w z=ltoL<;)wPdj=2XAmdA_kfbJJ=c=gn&}4pjAjZHVRly|2>awb<_E->XhPu4NMoJRIe+ev{Ngb49iEX?d?B zjOT5Xd#QI`WN&g!dxhWBus^}4&5r0RKa%~-a)0Gp{a2?p^*;Rm@$`xl&;5PMTh32B zdiFsc%UO|UXS8IapJuK&a{T#`iuXLH6`o8>x_kWfysxK=?aqByK5Wb%>$!$$zdfsQ zZA11e)iu}e#BJKzGk@as{qFX=nJR_K@4f!w?TeZ7UQPY_XR>#|qSX_NqYs?i$x-59*{W;vE@-uVugs{wd+52> zhr*>ban;Hhzb*Oxu0JMP)hks0)xK))g6rBZLmzYPi~QNvlpp#lc6`f4@y$y4rHUyTM4O_7$43rVNMy(XhhYIjHpq-&f@K&PKtaLK!N|bC3{9_~l*NMNKOH1C4+8^( z0s{ks0Ru>1B0~v72}2PB0|N&GdI|)kdF1p9;)BAm2ppoIbO*{CDGU&MVOD6s7z_*y zQVa|XY77hv+&EG&$OW)G0dheiLjgkpLp(zsI3`oUCNVJyFbIJC1?Dn<_#zAp3`{UF zMg|5(CJP21BrzKX1_l*~awu5=r9n2rnP3J7m;~9*zzojs1kG9i)q&qEu=@y_^?@j} z!0}4ZEC-PD!R8^yJxBoAk03c%7=q0ODFB5f)O?s2W*9;hVxd6hgDmHOCIy)JAiF@~ zAT6*sf{Br8J}5jv=Cd*|Frb#@TjaXpUn(;&fUqJ1Bd9FbPbo?)PfW^5)y+sL$kfkF zj?YOf%}dTmEz&E($0{)Kk)twU{N zL%-j<|99^FpL2`n-9E%vQ}ZMH{If;Z>tcilAq^Vt0Fob@lQ z=YMwMJ9)ZCW>eqCi9%ip9fvb7Dkbx)x^+k{aqU&B_KfgcqgvKlEvVDUbKz8WMEIj? zFSRWa|7fhddjDAQwKua0a%T5-+VB*9oWB2H`@MqajF#$_6BuGYpV;|irs*{g4n>>Q zA%5%T&AIpc;MCRAY8T!0dOJU^V(0!Jwfc{Ks@t`$iamX}rk&SD{`3ZmTM}MLniDT7 zEnYm0D|c2^@bmi?s+Qf6-!&91dN?~0*dKL?KRI&p<;!gIe?_m;=NJ9z>bBP18vpyq z-nzSq%`ty=-L8LI+{LHjV19o`bJ0$p#m@}fZV9jMjor5C*F;@@`>I-znX~WLJ}iC{ zJG<-;m@m(QN=mi$z_BK!8Nk7cj!9I^O5 zP1assJVhtzgq4-u@w3w`a-T8Qm!!K~Gdg{F15fAo$7#ps+@?e5Zz|MGy@+P>!X z*2&jnK22MFp5-v>dkbeR=dl0Z4wu`1Wcc+eHu=gnnZtd$eb4s_6`yhZ{4n~y?7xSr zzFwW)^}?OKyf>kp@3@3N(G|#B% z&ibjYd^7)E;p}j;jU5tBpUsY_|72#o`E>iK0*g-z4!`YfUVPH!-o)wqKc2ZiJLC1d za+Z^Q;#V}h92y%IZMosw`&Q%9-6Wn=4ra&Nr{X*H|LWGg=z95UZup(*=j+n%)crkU z9iD42UvH}Sw@26ao%#Rox1M|Gk5{VfZI0g@n7%C4jz4h9>(I<}+fH%Y$0buY+Mb;E z?VGLf{mmAA_>o0~-(u4R&fW{(jF-Jl zySAkAZ|cD_|IbZhFO#kPem;HQ?|=M#e+zH(9m%i#=VqTT1HsiTq z=SqWPiY`5+o-r-r4$ZP>Hq4yE)2^(pTyCj%{^Vzw=QfqEu2`<0yUqM;<;{zEC$x{Y z&9|O@TPMHjV#}`_1x?NO|IXh2U(UE|>0ht5Q>RbvNsjSX-E=!w>4#Ks`q~F8-kaU9 zn=<*AW7zFTv*5HI$1uTQ#Rn%c9(e9%h%}nD#N^`-CYiG*W7|#_Z?aK7A0w{gKaX{O z>73kaPHRJ_-xoUUA38Vp+Uz(}#|E9&|J4Wo%Pfh%&CZ(cY20oqxN`69ZA+fo{=0Hj z=JQ*&VCUjD-yX|kF2C8ev}sa6@+^n8M*&mj{rj@Sib0NpLyCpBO@K#=N2*1Dhs9xI z$HzcTp4nyp)`rENXUgk-v?%(PHS3Es4FE2P~qWkRRiGr5+jg>J0d&2i;7Z$!wRP6=bPm}^)Oms_>xPMo|l(YpV% zZKYg6w3fW25!0cOy{a&-WBXTZrNTe_uS^5<7(sMyIy%$K5OGIzH!7` z(D%l+@An;(h4-}CJpZvWcJ1xiruFtKZ2#SffAwVVY0ICHr*}S`wet8x$9+wwrHx+BX~FPSL6w%Fw7H!JPv zwUgbtXFYC9M$VeN)Uxu~3FH3LHhTOiGnLJhThmrPei6D}B~kTU>WU^qHo@QX`1d}X z{)~Bl^~(K^Zy%qO{$c0$+^U+*H_Y>Gr*8~R$W3uRv3J!x+mP6w=a0>};8iaD-ulVH zgI>Rb{NjGf3EZ&yp8PKVkLc7-3QCvm?z-;bxI3Do@d5Kab|;RC_l5U_V-!wX&j@(B zv%R+K#{9zWh~SX+=1zD9h>#j6dv~lW+3(2F|TA zS!guNzK-YDJK6PWk|zR!GMATbous2b{q*cb7LoIITC%DuC^QLXEcXpqD8irIbM%&6 zan*y8%X_k>J9cHgU|JfRz;&SO`h?v*ZpCsnlFpq13M|g^R++uxojEtl*YB;t@rwE9 zdvyDclq$`g-FA9^(fLN^7lO6wUTxv-I?hkHj9>ChJd#sapssPP{ekNl@1D}|&OqK< zLKEsZ8WWBz(psUFwB!P(*v93*16X-?9a!{k^Y?`bAsj5%Q>U@>F3Xmv{b;uoRV4yUyqpBH2;?@x+@lpDKfyHL*WzCUl*JlXDXrO@Je zqQRwQX~8!IeLtRF@$h@Z(UVuM_Xk7=`kM zv-efo9T(NKEk2rk{dergT^HtU&^W?XP-S&BIkG?V+|#4!GEZjOTX;+kTfHoQ?}v!s z%aO**Hzyk17F_lu|B&ZY{p>jvw-em*_P*8o6%KH|AhzA!X*Re#&X!|xPte|lA#(epi*GmiVGVHbz%ic;r3 zr5{|+N|a-EE!?VdW>?gbRk3cR#?j%EovtUc$A`RK_}j`tmBn%IM2&3@`~EHRPT%>p z?Q+Gz*Z-B?{5meVdHa{6ht(dXW*gtTebcAzzo2y4nY&xw#%=!kuTo%r=I!LgIq?m8 z|DW!3DgSGFnSF0(#6Z2dYj-?wK%!sLrj1aI7_#{Xp3qj>kXZ|=R1 ztUne$QVAcF` z@YT!UkBo+0od*{ESm3d)qF;OJ;%g5Az8;hR`@rDelc(}u^=rP}UHj%y?J8!vhODQFZn#TH#2x8CCw*0{qy{ywROMPm%r1#B>($}xW(_Yw=MQ;nqFmb zYl^SsmYLSd6aMxta(Cl-HRbl1d%yHD&)q)x^6tCbY<{`F=f1VSD!c7$YHw^^&Uo`e zdZ2NXR8Drx$)z?cczBLYIn}0Nc%wt9SyJG#ZeVJorLEy5J&RQWRl7t)?yo%@BUi9< zQW?|Sumx8?Eb(=|=Vh{X`ckv|_pEJgKVO?4S9jmMuHZlGlCvMSq-Ou|S66dmKfbv1 z)~C;#HveNd`!j9E{M^|q=e`upuRYlne#K#5%~83z^L>AQzQ4QrzD`;3TV9Ec$8Kd6 z>?j9^U1|_eZwt~~7P9e3=RpbQIT4FyEN$Zbv1i5i znh!gCUT%?{yG_~OHZ$5Yy3eNBhCf1hqKcsKX}5^l3p>KvLbB%0U0;96^V|Dtc@h8i zioSj-svWlW#3Pl?*mq0r`ed3&EsXR&d?iIHw|>RPq|kp$LpE-U3H?9)eMLfMNA2rr zpKts;z%g^@@eadAfw|Lv@8o>H`OFOqMrK7tf7`D=)3%kbnzZ@;;6%Fex#y*uZm?LGTui~8G&O;^5iUyQqv zae;O3q^81ST!A?rUPr@Lxr7|o(Y_YF>abz+VMFJ|f=^c|YFhW)lywoZ-g$N37t#7( zAMXEspwDi$V&fL+>O~rx7IB@kymV#emZzm@+mp}tmCV&rQB{6DKjdzBbIkHn2Ocm) zZ#%d0vu5&P>1kJIl}<_M@eG{0PJ``I-}bLN=d6@{`^tRw{wJdTmH)a{zMQ=xtbIvH z+r+1bx=NLAR7|nZ6THdNq~OA$Um?yB$8k5zjbj00(fr3XaZBDl;5eu(vS@u&iL5J^ zaAbw@QEUHcm7BJ3Y%epuu%rP=|B|z8 z$C?a%dqjIgs*=|nEa6?WQ7oG$sq4e5PD_m@O+A+n-kZYouU22*%5&~<%y!mG*{x5! z=4tD-`gw1;%=2Vt{=xTb@9pAVU9D`*zTSFfrN~{M$P2T#On%2bf$3?+h7E437Nj}a z{C*!HaN*Ia?3APr_bOr>dXBOML>nk|h&8>birt&!cQJhbI)l|5i^QG>7fly*XXbDU zxc_;k_nG8=#@7EY<+__zEXWdz?%2?ztuv$Xkd@AifY^}VnGFwIFDy!a>6{(kz*3;- zd_+Y3YVpp6?%9z+K3yDAP6ryDU40JBV%S!$*|o{3sl@*N-*c))IR;4ukzR}UPCuB! zyZEW|P2269$y19mS1@S(SgI~8c4lGO;oxd<->n&!S1metLcuw*iOA&)lB(YwEl6h1DD-`?4=B(lZDOh!k%O`KID3;qA5`bl=_mCY&`_ z)9WLH+a%Y-MSmZ2-Rf6x`26|&^i2uvO;sVGAxoDt{M~Rlz$a`=bU^!q@5dD)b_-3& z({}QiZ~C_?)3aA3!1dlHNr&jzS=ow@F0DUfcsI(&J4eF4buyoQzw$LZ?(9eJ>Z%Xu zY&l*X>-P3i(%DVP7S%WBe}41$Uqkf!zkJf$<6N!p-_}npDeSzoHkxZK*Vj#vubtO_ zm+R8ovq{6~*Mly7p(zL3H{_eHuQ)6@L375)c!6Qtj$E_Ca}d7{dy@OT)KW; znV3~Aw}Wn9{sNn_D#3Z)4m`Jf&pN#;y5=|MkbTYX;Fv!f%{LT3|Mre`gX{YALYa5@ z1~;!tWh*=UxXBy-ahADVKf9TqvE7zN9dGd&A|lF$S5I{@RV1w2xtU|8-D>f2-d(>x za3?Q~o1P%w5chYE{-*5naXk@hdIP7HeeY(e;A!4c-l|={$D`f(g6di>kxw>HEsyVQ z?!H$esW$%b*5?`~!?s1tXSw|_M zC-w6)_R9C|dJpc_f2?tv#y$W4iSrfT-`CF+ef;{vy;5i9n0*0eU$zGRns=6$CrVrC zW7GFYhm9vRnpRakw_bZ^YhdZ`^GOVg51$Sc4dqd~^u{%)uynfFrWe=rkM=lhR#m$3 zUB>&ipHka`R>|6DoX-ni89pz#EBpV1`MN*e>vsGq-t%kD|I;7+?RwY8eJjk{@z>hs z?@RyZ!Sa6?-DN-ZFbVK-l)T>V^t$WP;)1KO*97)t1qS^{Q~O1ZQ7$Cao>8QowAevilydT`(86FTB~=qwDWbGKpX2RuSButDy8UWXM*pa z*z9jLU;h3F)qRCW*?)YQo^JVT#p9oURPR5Zp*-)Zzk=7qMDgYS|8;2S@aev6-_cPb zUA*^=miLWo`x_WJwncBdp|tkx`po=Yn%6rHb_l-YireTMYr?3jp_IfnPc5rtqV3!G zsdaA`|DJ0mxlzHDh=qVcN~* z@K$=;@vi6l3^Gdy!4jq=@q0ZiqwdOGa(c1aYyUz04nCflT?*Us6-qPR^uB}!^!jj!@rJcj{Ac(o zE+WzFA;`=v>G?hNu#%wr?qY}7sONvSt3T{!`D8!)H&?zF%#Uhz%ZWswml+mkG4 z=qmR5THmgMMo~wW^^V3NhHfQ>*^B?ic(Z4%5k$9VjnKLrpAAd-<%OR_uG}% z`}?j;`u=J?+sgRB6Um2`z6dV$*Vtm>k{92<ifCUYdmjF>fI(3>$lbD(pIJ4j4NiCaH#PlY%%H=<4uXZVi3LKQr9YtBN4N|uoiu6 zF==s_6!PO?>HNpP=Et`Nq%2+FI(KV4-&^%HQ&1ZRXN9)Ut~y(7Uo5qH zTK}=Nd$?vP=@_^)CoP_F^IDqS&#Tu9e)i8lx%&Lx3CrjGnWMyH^5*b_<4wUo798R; ziRN9m<5hOe`&Ie|w)=aP`=YioybxMw=8`r;AnkEbwq<1Gy*sO>u9I20E;`Ih=<7Xi z?d(aWw>4K@-kfCi zdAFyzzvkn1i*FaiKddUgch=B-Zj6Y}J>4I&u?7!TF(&I|&ibT(y{7y9zZak1%-cPG z>gr<=wKv#$cv;p+-h5TCZEsAJ>hX0p>M`|yWqGEBG4{XXzVNI0deM(SOR9Gi;HB}a&Ho^!^R}#b-F3y5xa@7yzjq7w zdY}5`9JZks;mD+^v=?TYv6^=C4V{}tO`|L>@ZXVb>E6KZ#gewY6FTW)u5TCDlzth)mKaohO}Tg4)o z#ct2PuJbkXQvPO*h0C&DZZTOSyXr(_`@R!34?a)-&A!d7c5mR**Y-A-xBcmv*7j+E zRK~gIJ!?CEK1(_lUz+aUbK}g_ z@(H_j=2Tw$tg=m6vE|Q_ZL{~J?knCeI=O4(rQB$pHJn`CryAqTOw5$trr2ysoum%pXn|1nHC^Q{Zn3V3OK%o^6gjHh(s5*6(5F>7_y1-5I02X~?foQj)z@EfS;ner(~jS0&Pz*s-1+D3@%Mkui%+^= z@U_|Iz3TDe`_0=wUp^frQ~gRSXHM(g&);9xTCZPn@#exvH7gsFGe3S5&7HIJ^cAV% z-92Iax!a}He_lA(;u;nrAFcM~)ZRq`X2&)p9krOWWyad~Nd+p2S!KrA*Zd4NCT)oL zpZub4?NScy$Tw@bF6`ltaO;eicK+6PxCkW>24dWUwBw43q3`St-s=mo*N1UdmaMZ+pM<^{cRLub=FYiP;%sI8jts<6V$hm>AokO;^5uxh1md=#C(R zC7BBiS*24CihMg}y>R;>pOZ%YEsXMp%jWtSXKh)Q@3r}y_f@krwn?H!rjeIluDpBa zlm5%c+b8_d-}j{|<^u?|vQ6WZhS%qw zZ?(S{w`a!Y2-)mk^G-7G&bP5?YXT-+u!U~s1I2;Yvr+= zyjR6@6*x8*&wH(R|Kkr`mGXT%>Q_hfJZR#sPJPUL|NiH@=a)R+_c!^x)%MFP>r3kD z-UsTHFSOclX3FG`i$op!_$3cMWs!;MS>~KSwOufnXMeA#LDQ_f{^hrIUa#BRx5y!- zsOYw$_wBBe|7`ud=Ks6nS{`3`?S0QoQ|;^C*G@X8F7T33=jm5D(09DwkwHB6m-Laq z&YQRR=A2&DfpaN23&Av1lWS=@~3 zug7jKi@2?EEMcwcroipW8XAoPGCRFrmPDCIeZG}bvcz!uxvA~e&;Oidf4@iWS;gP# z`H3bHu3XnOr6#9p{{OPF`k(vrAK7;UzE3QVJy>n`v8b*6+QqE*Ph9nDr~P}f>Nd!# zpihB8W@pYlyONgOaiw!|tjf}kPY#_ynhRrA1!$>SnMB>4p|bRjgmq3&V0LbSZor=d`D_=Gw6KvenL;?=6v8uaK1SjeYvPO6~)%^=xdV*6{7z9{a&d@9*x@ z9Bwa6{yl#nGr^iqf9D7GJICN`8xpwoh&Z!<58@Xn*w7;ME)qSqO=Y{)!*)ShAoE7@>)^YQHQP0yY82Y{S z{wSVwIP%o~haU6Q8I4&^Qvz0p=r$`GDkmE*KAq&ICe-Gpw8~lV>cJVO9DG*FOjcoj zc(8M~rfJwxYqLpSksgy=yqC6|a_D>&F!NQw%*KO4Q?q zViCA$(xptt<)KS5g%3oyUT(Sgag|JYQm9#84x3NLZ4Uv#t=l5bNFH?(krXhPsMyD= z82@Tx&XSs6w)XY`%S#_H{Q6UV@5HB2QMGbkQSIPf0oAq>l50#|1S3{UtiG`#B4X+` z-;MW9a4c=?_!YA(;YNW$gtydjRte9WL0XZIR1TFf7M+_U;k+-yN5^dg`LbaJ$2zO=FvyA5Wmt6n34Z zn^e1Wj?H>h_Wq7Q_tEt3cD|<0FP)qM2R_GdmPuyK=2cpBOk`f_#~Dt^3q!*8Y(M(e zXJt~)ady=%o~4b9O({n_mppBpp%HjW^mc4c+?p=kV-{@Q2d~uKP2ss_*T`6Pv3q{w z%PI-M%iBn%*qinOt*I;PK(dQ@lh^O*MMz)#K8)B*#;z zxi_c$YqsI4o9qiT7G0fbzkp#T^Uu_$DFW}Cs@Ts3iZ9t8uyMt5F=kdZE`=@5C#1XF z3$5(7Ifbpi(_m;cb4t>q76J96Ue(f~TW4-e(8*e(VD={9>YIzl^j%aho4mCtl)RF) z%;Nc>BOe@D&emVcYtwalDbQlWk{_t7#Fm}gvG_*0lln)i|9_MH;><+VH)dX26d*N0 z;3ngdmV6!_wfv3SZ1R#%9$nR$6Vmf~LV~Z-+N$oD%j@GmoO^7*7A$*y&&QL(;tSs1 zmRGF&VRg6gh32uJC8th(c-`xAUs@{0FfXulR`)~};i)Y;zCAwi>5GrG&fTWodECU$ zibvl#@%)p+%WlrxnO{-x^tIQ=2lxAV*2`=TToNeKz0ojo)~uN`ZMU4A$1hX!Lh*md z+P+iH{P*9jWyRRQuH8bJa zq$QJ73zsUM_&ayY&%b_F-@kQkY}TJWJ8$;e&Hop=udmZIzgr@`e9pdEzv~RE51f11 z@Q-!X_PQS-VQVesRLlJnT+qWWpY`fz&po@(9d;$U7rW=1TRoa{dVjs|;=(HnuNK^C zD|Itj(Ghsnp;RL=r=~~OI&Z&{>-tQ~U>{A^7^Z(1JarnzCa0j&5$kWx&Wt~$(6Y4@CoijSqleicd zKv)s7=o7I}C_Fdc)(3ofv)5RnL+cxj@hYLL~mzOds49$F%|yCz>U z((=5L!JzCV!1++npi`xV<+1Mx>+e6S=RCKo{@P#szIBTCQ?0wk)#u)CKYuU1_Ii2k z^;^$#X1G*8dth4sYySDU<$qGH?J+2N8+pI}#XQb?Y-TvGzVE&{1Up5x6 zzmaW6j6Iv&_HuE#GFl&muVuE|HS`V=kG~5tf|!WO37v0 z|9^_Nr^T7`EqnRy&yzJXZTHWxP3G%N5t-R_VXdUAkth#uLFm;rJ|aiw?ATHBiFx+D z-;a08+oexCQT8rk?~}LOHyOYC*Uvn0!rW1tbD`J@hA<;ma|jBw3l3c^U8ubMQuX&g zKknwUe3|{G_;K_68d)FdY3I4DIRhtT$wZwrog~D@87LrJ=2k5s+-m4KWzvypDknWI z^UO|-$Tsy2j#1-2KCjwkf5EZ*FUwdNwra|4uYc{sP}6Xh+1&8_ZsDu-+WdwU;qBL0 ztJ#0mSr)k1l^6V8^IrDln>~9XzSxJnKfB_JZI`M5vl5raE0$2!#R^^eOi2YZlY~_> z#8L|C6*rshse63DcW-ah7PG_gJIX(ZZhrK#SbondTmM-0g17hQm#k^4+w{EacgW=I zklJ7T-#0(M&dfh&Tgtm;7US!GUL5S*m3+Fvq&+F*;Aftri`t({A8BBEWpX`m(UFNq zmr1&{WG!A$>^DbitK9#kmCMsl``eVhx_h;1`h6+RFAnk1^D5tbSeYFzbHni5g^23P z(+e*jN@4iWVb#Z6ZFRXj?)lu{w7myf!@r$U4fl8bu%T12S2mN!EHvlYhRn9hJVy>n zL~ES0oOI&kiH64y?mawq(C+_^sB-UJT8f#EmY7btD09jyS)z||$t%^Fp#iF!`_8Nq z-}WbB?}W0;kHlG$d6X6(>00or;}FM0AtlLunn}jy+vb@R9+@a)!W+0`U5H3vdxJwB z*Og*nCMVaqr_(K2OGS*jm#jQo(Zyj=sL|kbaJA9eZ8N^_uh#56cpC^;DVWlEsw4gYA-Y}bQX9evA~B>(_;40NVk0!!aa@iUd7xq zd~4%p_s@UE)!i92e_PY9yp*mFa1brR>)_U6Py*f zhd=bMousz^z1wE@s~ke9Wmk5edANzms7OkD&-;7<#pf|8Q>qv4Hk-UzJof0ty{>VY z`&%@lu4wKH4_(SSD|n6HRm&r(UJ@z~TUrCBdd!_v?%*q^QhrWz)uA=_j4fVo@!s)u z_4H4FrXA;8bZ*DINhWFMS6lyl6gywZ6TjoFW>|_7+O5UdW+hf={yKKJmUvm8Nuv~@3PkjZ}g!(XwurAj)mw8UW>Q%;W zdFzjH%PyS^4g{?o%I1kXH>0ohZG^bjQ7v_u?K32emj$mc>u+Z4c*GDhPj^=4zstQ$ zfkg+c89mfkRFo3e{i&H4?Ajx_{?IG8;*a%PYkI5qihZs%*sRgC>SXt_Mf3FI^j=+W zI5^ut#bb${xgD3+oXsz1tP2q>d1NkTdu-#MC>_ya^*I4rCIRh90cVXXObTrnWG~g% zDK9YCP@S8#LGy;hrYR4-u8U|t>gWD@_3-V8pBIYS+WO_VADcFDG?_`yi#^V~`u&24 zH-}a>3A?P0yzsH6Dq3Mr{)FN|b4~XoX+PyyF%Ps9U+poo*U@FrGB=;{_x8(HvZHlt#*Hc+TuSH-CemTvQcUC&#&jgQZJ_$BBb_ByYc04IyC2^0bLsEtZ>xT7dM&m}T?+(QB#C%GC}B^&elp8b4i^tKfck-PdUQ zoqx8M@N72PANIP=C^v0pRBR@nr5l^BXF^x-?&qhE=U0TMK7aQ-c45r2ApKySDy<)r z%2I;ZrT(R;iF+Lr@oxHPW6Bn&ZXzqvEq1=W=xllFdXKe%N51!_WHy!=IcC=HKG*KD zMD^H%N~NQl+;@g8h|z3k@Bfy$HL$dIUQyI*y?{)SEvp{|zcQ+rSMu*ZL+PxKpLAY& z^?VEXv@A&H8h`4#oni^!3hPD0v~QL#^P4N^^WN66s`~i@@jq)jd;VNsGH2eIC&l(b z&w4J4pZmJ!)6ad^MO_{L9BJ#7=G*@xqVE4M{j=t_y~6(%TnUYSH1pDh0}~HN@Bj8w zz5V~6U&aP?XSV-;`t*L*<9deHc_MuKCr)3EyMcOY!TzNOg&7z?SdoDlbtQW)mIf*V z1Gcs72B7U7;5KSF)6aGW2H_4+8#UwJ)~L)avY}J|eY0HtsxSZC2P4NrQw&-hntg-> zraH0&IxKN?35Y%zws87_MNM3nGD2oGC^fCIaOm!GxV+%Z1s_Gl76wHh&PvXcvyWHj z-(6+({LNkaSNFs(#NAyoG3xc2((tw6p}%ie*WbVQdye~!bFVMnt3R^vuKe-SA0Ka6 zSC#Ys!-rWxhWk@%7I}4-?Q@U%7;B^<abyf+v1TdRX>W z@{Y~(2P=Qua7gSbc-gz@iFWq=ZMJFEjC(fav3%aWeraMxWvy<#{fT7fM|V8u{?yzx zb$`Fe<9VTnSPrNDI`*Xa{L*wLl@B4InO4EEU%R7!w%z}B>hJb>dve~s+80>v@h4$l z!U{9TO}5`Id}G>?b2I6Q*UGNfeT%i^H{3q7ZTYFS4`(j<@cQDb33k61WFM?36X)OE z@GxDYMbUXxOALR?ty^_g``$0rb$=bR@j-O)T5-F-Dd(5&-}`E*?X6vhzFNP#wd}yG z`d@EN-^(@pm-;DiEacckH#}KwWeZhj; z3q2P1h?%k$SMbbE7rXGwdf}-gw!$u#TLzPzd{h+_91>;LCkd=Scu48vMitMwk5w9d z=XR+b33W?7`I7z4$FtG%?1Ol}NURJ~V&r(!m_K*ptN&ZP_sc$(m;A>e!yFjp8gAjr z%4Opr{d~)XFTa(gZk_Lb*s*Gg^KPXlE4mn0C2?oHaF#RWQHhgdgmjuT2pS*j)~G`VthONhzw@tl^^`?_Ie`{tgipv6%pd>v(GQ<<;X zL@Rexb=-W#`n&YR>hr!2CM-^W(7FH1|MlBeZf92LlhH75Fe(dkf2-iTOiHSquo+oK+N>E15|^-pgp zs6Urlymjr?U*7x5v}7do`|NtRp0{hC_abyp{rtxTSKi#dFV*N^e8E zQm@X9-_HeaNP4?i>%v^Q1+G_L9f?`t!l39X=fdL1@`>@0#o0@~98KoT84Vxg?itSc z+`!LJ@r0Z4Qp>|Ns)>!hPcKO8t(>*;?yb8u_a|x{Y>|3&-NA!(-`Vqg-yLncHhfvv z{NX9T{&}Sji>%pMuXZvhM#w~ZT{-;q>b@PNGV8(==bq!;a%#EDv(KHfzjg*Zni@1q z#8I%(gHb^s)q9Cx1Y-otC6ofHvj>~N4sNHa)p*puGD z(ZO>NNA#M$zqeHoRJ>dsXh|o8|T` zi_`A!>}6hk$v^4)0|%`K9*j!1ExY$GORx)``f;22vAFu=gzmkI)-L{AK6_qv_UoIw zUQI7NJ7469u;vzysZqS2T$>G+FW_~QW?i~1(3?d-&_MZdqp-zkk7PHQ$}3uYR|_Ym zC@py<5&3#C_r3b0@1d8|x7U`mM+X1jt9^a$d*8Tc_x9Ocy&e4fX=eM`A32O~ORmc5 zd%T~P#rELcb^E4o(=umu)XY+g4?Y#^$g%h98n@$YnXiu(+Wd4l|4;n?izVB7Utg)^ zzx!8a@0S{R-Y;PSSA!~!PjGv`zL$wHK+Rj7!$F}UPixWvOa5;TVirykQd_n?O+EI; zPixB4*5B`HazC=#-~9V-=I2xF)4g;vL)NZ7RpI(Icirmn`Lit~7Cdd;6r?NL#mnIG zw|HaL)3Zv4X58EOJ~7aUN9nA<;uxiu3H7=qg?aNu{c~o7Wq4kFGv;4=B<=lD)Gwq+pX8~ z%ih`UFy&(Gk6Ducj$nT`( z#mqAvNqT)>Hv3kwPdJ^;Rm7FJLa;?@=Onj{XAej)FBO?`>B6Z4zeUbjYjE9HStxg| zYRO7>sdVji#*d^t_euqr1b)xVvfa#bwrbME)ib9ZxtZT;u`->ZgCmMvAaTO4)$g1X zBrRvgteRo`h)b2}z0IRt^F!wNbeh;iyL>Ht@r$Is%MnT4|wpQucZ$&#ACQc}?L8SD98D}(%VN&=FnUM+ugl6}X*`$`Fk zVh^o;zljseZM|~sV(2~AoOx*_@N>gf#rZR}1O+2(oF*S%G}r&YyyAY>|2MiL zHoj9^Q~6deW9s=m`e(az{WNnGMbf^{zId|1Awl}R=(kB*FYB+XXNZ!_5M@v>?+lY- z5nyU$ay^#Hs>%Ftj&nxW3Z5+spR0Dy$vhdOu`h6ml&BQfyv>JFOBzZR^^|JM_LMjt z@n7bCZpy+bZQoa>%s*$;U|b_0SaxpdIZK1;iu*Iz<*FX3Wr#Yh77#cxnYBYYinl@J zDQB0|J%*MEwAaQaM`>e%Wc6)!Bzn-OARdsp$6Mj%_~u}*!sCv z2!2{N(f53c-19SenUky^yUlTVk@Nak__x|Zt<@6dD4H^c1D&j z-}8^Y|Myty%a^ssCbriyJ^y=Is70-Rw_Kf|nW)1399xM)hknM`DR|^XbER@Gk=cC2 zi_wKi@EpUI?S98URIoYLG;>c4@1 z{@G7|^zV3{WBO^nin6gC9gO}V_3gF&_4H>qKPhq%iTCZ@zwizciT>$sv4>B_m&-xi7O z_kJha?0RGt^KkB*+xF8_!O-)&&HS` zFhImCnv>PR)Xu@P`RkEC&$dOlD69*7v+b$<1qSuWD-A8zrwRU*Zs@(ovg+ct6wB4K zYKj#+T^&A&r3<=ftabIw+QQa-PU-HK89`DmEUu^06i?qfzAw;0f#pX^w9o9fZS6t4 zd_K?2ea|uF^9G8Yd}g}y?>Q!=UV{wA&Zf=x(jC7D1l|$gT&wYg)%I%E;(!yZ9odT` zxo(CWFcZmlUSimGlac3qLwSS6`v#MVolg6B9L$$YSs2Q9`0?xyoA1x*@11?kS(CRx zt$5E)N&#MnquTG^uVeoA>B3nJ(dY*^%@;{D1)NzMZF}MI<%FgFrvfrmKeFDa z5`5CF{qJkIdf&f4^Co*}s(!6nEPeOGDQS=30(W=wHN1JUx4JfqZq+M0_jAX&ou_u> z{QUejdV1ZTEARNj9y=b%<7Qw^Yc!O4U6ptA)Y9^Y6I_=+Om?pqyuAEuMfb&-mn18W zSA6(*yWsWw=}D(AaKD&$lW$SdHCFScH33gH{z^!?9J&1Fid@g^#9v9X!vfdy*jtzh z#eTWHTHbY^Pe$(f#~)w+GG=J6uMlULkgd17``5N_p`TjUCGXmOyI+~>ThHsV$(w)f zUbpLAH}j#c!wWigr3wj7NHMa=_A{TfQf#&P-Hxl*?=*gY_t$ey*&$YrCV^uq((?U! z``NeOE#}?Ux6bMM7o%@8EZOhkU`g}G_K2=j;-0{o9`r5ZUy^_VJGTvt} zS2up&r>M>HURES(&PU_3&w6Li-}7?0zx=1=$MwTj&#P&VbcnhGsI015Pb8iaWj~Gd4hN^#_;URj*j@ZQe1ZreWqP zvx%$zHO}jAeDTfT%3A2M|F$M^V3S7&B@EI7H{W$E>8_i7hU{uu1_bdw0TE=%gcNlYIYOjUz6E6vTE zknw4rr{>g?is`ov?{Ve4QuS@w^Vp(q_4D#={{JgQRK2$6JbOOB}noKb=aAaWR zWo(?87_9wuh1b>c9W9@T~m*w@-Rp zl5(-}YSnx-N&oH<{XP2@eRU6?zq#)7Gv44sF@auBg{GcPee&7$$!FHgOznJ*rU1#c zo-I+2E#8%!Q}We)70CPeakAdJgC0>kHqLneqbDG%X_}*7&LIIufhUX?f1I6~eeb8? z>!r{C3dYA5*{nJ8VouPcr}}prCr)I%Y-#)8#`FML@g5$(goM{E8`v95gL<=$>)EW_ zb}_cvJF9QUC+YQTk)fubR5={^i2m!djTJ@lAX7 zy`SHzKJR+el%xHAdHoGZhYrilVn>f|^0Qw4G$2paMbs@s@Jcq*^hrxLZu_D!}#fA7y? z1H}bA&0Cf$ET5>K!P3}f$Ro_rA+w0HkRr?|IVUnpQ>tipR=z|FOTI<=14rJ5YOW(Ewt2_`4ekz(#-`ok_4XhPH^y= z>#$O_KgMoBl6ONf&C2BI` zo7c_dSZlB<*mCv!mDk+P-75aS?qBcvzJP!=fp@=s7xiu}eY^H9d&#!*S=ssXHNBp? zFIw?wt*!XG`p4@&moMj)4lkI&|KZPM<@a*z3<(z(F>iRkkKMESwa%`8m$v5X#P56b zV&cP#&be#sre3-Hy}v)d_WeHgcb}McIP~B7t~33M%{z^^E+T#Rtb$^9Z-4rNLk({?3>GdCy(JANk3PRP}U!NSwnx1o&dYfsG`|ox4qh^0Fs?B}WpQKhRul=+62HWifVYY(P$J#em zMsgM}|0>MN`Mk+4#7lUF#Og=Q?stAqx^q1F*WVp`e?`}9_pgeZzB;JMKYwv)O|ah+)11ty57+j>T60@&q+1>;BxkDUBGv5J@Jo4nh*59>pEIlNv}RDKQppJ z`u*?Q*QfEXu?h-&_voOn^5(SjFQ0E*xT@lNWBG#mDE@oJC*m0yLjOojt7mRtuyC2t z$M}cQG)JL(hJLv|5crS`t&o#ew~$zt4qSy$nOf@ zVVJ9a>C)28VkelFpM4c;Q2Xo4X1*CF|6`>OZ?gDQdtl!0{BEOreM)Ig4$4QhI9LuH zoaEBWci=pOPRRi$wgapTwH`;9Y_x=8e56dpIDT;cE}W#=at(1xeGI z#pAj*mQIfS|H%4ixPRQI#hrrR+b_BB28!(ctX_WS+q+)p*KauATl?tE*vH`f%rNQ7 zl0z{|KeFuFxV_upu$BwkjFg7fJj*@~_m3y<)LJOGpRi2GmrmTInb0-2wf3%)U7x6= zrMt`mooK6~Z|@mYF0AN`KfmX;Zm;~mM<+5e*Izw-JmYfK_q{uBpS~^kE892j$5TV= z4?CCz<1D#m>~wHEQmi=TacKY7WL1ae)m`$EvOU)`*9K~c?Ne0{o44ct+HH1oSh}zD zcP8ofuK<<}MGk z&lT~1yZxT7uHVt@+{RyL+YHa1zI1-o?&I$RjThNf_x*h!_*nRA|N3a#Cf?PLcK`b{ z`&Q&`*$b-E?oHfrb&u}5y_RkppDz{X-y6gA<5a7=qTW+MUn#~Vj#Dlht+amKV^#M` zX5X6nuHOy{)|V%f5%NoXfBB z?fdrpFn{d}>CI11Exq<^mrT_%k3WBQ_wOqbVPMdf*zx5*pXzhhdp`+oMx?XvKpG4oWQ!=NL~3GPE%t4(t)^ zNoHUxwJ?aburM%e5$RBf5nu9}t@xp0^3|zZKRx-prshYzyW26leQyrxRy=CF^_=_Y zp_!G5Pj1FnT$uNxGgd+6fzHEC#d4LYnh!dEq~yrIZ5Fg?n;-knJm(J2uS4$fs`h2C znr(}}OYO^iSY})OX6s>_&6l?@r^+@nZJ6V}pOq;wKvXK#V#c#xYX&hk&Ql(Nt;~nD z4;(sl(BkYh$@I0%uN@}6ZWKA{>+Bp|H9umP<^D$#UzLj2MaqBQ`MLUy>3-R=^#4WH zbx-CMUOB}S|8IVEoY>3@r_=wcFYYreSuD@}g>{F1oov}FyNIV3li3RHpZWAma{8)! zrtUtc{gc|`ULDKP7C5P8Hqnh!ao2?7A}lZ3T=*JT4j6bC^p(yy5EYSe!{^5|-8ID< zt{sYGPhR6YnZ1J=e3sx8RS1SdCaiR=gI?_gHr96`}TaE zINO4?3h5-j_d7#2C>qx1pG&Or^CWWmcA-u^aWBU^}B-X<3?>d`j5+S)39HR}5MT z*SL1A;eK8ZC&ts(<|rvu8nGnd#+fpepeqT9o831m6i9m&F&NKaoxwUM z`8_X>0H+Ys8mH_fW$*R;Waq7V-TL@<+O_P%(nTqA%W^o*y{wPtuQ0F`{M^*M@#Nih z-$!OS$MZvla)egZ?vTGdkNrU20ijn%nF1Z`+dfz@JrP{M&B%3=S4cuis<5qQN9ALl zrqh%1l)pwvNSw1dAQAC8zW1WRtwR#r*}6St8?GH(!gnkpcJ&Q~b)P~HzwY{PTE2Xf z+%j3!VhYcU-JdimU z*&u6l`unx>eVhI`B&0QHPUtf6{Qm4+=F;Nud&bOty(&z(altGMK2aZz?DrSiSNp_w z$MPB*4n?griMHgzpnK3<%;fva#vL^Ulb&Hc*?w^n`ZGuyWU+c)|@G?FlpH#=T*~qnk`)J_9^CpAo#xD+?Qw&S|SKc{b7y0*g$FDPTuhT2;&(c{Z&HY@)=UaWY>>SoDo{Bl*-)4SQ zePi_3c82{=jw_crb2N3C6`P8u%}LGHeL8n*ciFw&Wt9;LX<{qWrNn^Q@JvACA${AJ8vlzQ>q%-tDX?#W-Hmg`uPTnUlO~XY~ygz z|JAhDK$3m&ypROb!x_wLdAEFYU$yoBelw3?-np}$C6<&)a4%l_o#XNc9=ACm7v|p= z-(1b_BJw36C{d6v?hC8WDP6Z&*J|Pzv=v)KRpRd(hsqmU2v~|{3SR%SInw$6EyWkR zmiM`>P1=}#eY!x~l4&sN*0;+ zWq;)g%}(~1B(>%nlV<4l21$V>3K|Z&vParJpdb8q*UEtY4Tef`49b0Uk356-?e z@o&yQ-_30wcpIx>c`^u3e}8- zB&Um}>8|_tTC1UhLEsUWV}OdAdB2jtUUy6H&y!3_zy4}jab10n!q&zIHf^i;AAIZ$ zKN!_~@KVs9+`rQUug-qh({Rmda{_m=$(Cz&JWK6lBy%TZJ-(mcGXKo4(!<;(%7W25 zd=8Y`{WP`N_0B8gvEiK`+r2lG{m5fk#F6aN)*-@B#9(}IZ}&&BmY*!=qSjP9J+xi^ z{=@pnj=LK^u}H|ZFOm6lRK|5v_i;6|c=@Nh`c;~|zwRjMm_1$f$C0r0kt{QqdJ8!+ z1zGP3Oj2CR(&4aRTXe31Sc-&PLEq-@F==*JI> zqMaOGn)5%*Z7^oaOp4taB)Ifr$%?2&%#$z9TfS@A^p=2gSG~FH|6bcvZ_B^oT4PJ& zMVEvR52h`=d2E++kD17E(TT6DdYV|8nVRt)TuN6q~ zIelwuUdS#x_K0L7mnlc=tL=n^2cPVdTcPlUEA6EwcbY{RlgqTZ zuhY}{XIcG?vsmsL{ws{3CHemL&->>wP1@4+$|9jxwDu?Sfxh_EEpPoU-%!o$-4kFD z++;7M*c5Rvk~^6rR!t*<;&;L}K@cv@neEoWEzETg#J%1E4cS)u)kSAp;RpSCC9h3 z@l8*kC0AtJEV@ymJSA$uzkP4AHoy1Fw>!P#((R55|Wx(gMP2X3F zmUWzH=3nHK!K3v5)k;?R$ugVk&lieKlUjf8*WA*5%Qp1>|NY_V>vlI*-wnq$FYvs( z-?(ysW++F+JeS7m6{|DLeKNY$X&2`iQ7T5`h9J?#vc1c%4>Tbun z7b4zh(H}|CI+g>O~m>90X1ODb}?X&%Pfm^0r?+>)-E7evW=`yH(bH z`W4u)_SQ|Qc9Txyh?|@0(r?MxRDX3Ukjy$cDcyR?6+SC&!(A^$C3jxi@Z(wax~IIn z`U!b$O6yhTOYgZP^?vsmVVyOmN8%j2({xsxVQARFwd+N6`UCFyQf*Eh4Z999aJHyu z`qw^O$I#IGx~<^j+kNvtOt$8)O5}<-JD=(ET#4r_(La20j>Jzqp0tNOevwSoN{7jr z7uK~Iimr(a&M$Z$vNmVqd#f)!GSN5RuW2&uyS1{hjImtiT4@mHwL>L)()Vtfb*M}> z@%Ztoa~aQlcJStB*w2W*H)*c5+cQJnrIHrMkN9lp;|_W`(|&KYR13@0fMeUw&Gww7 zb!w4Lb%BC-fzkOT=RfWGRodloLhD-c)5ot34WpVT9H_kh{{MN+2OEEwG_bbaeaoS! z=s2TU`H8|ik;?@P-;>zRFG;Rg{y?Ya3j2}?0(Xx}Tr*rcH7?{;^fIIFlR3=ECJ9@b zFWNnmKJ43VzHF{T_L0|RcE=CB^7*kY@on$??Xq6R%a(FXKUzEe@7p$|n6_C_&rfXF z&z~3<`C;KXPPUe`*9mLp?LO=no=~#Gsrl)uIlk!|T1?oK#UkukD-x_e-`(psrDw{a zTS5G1=h*tPEaY!=Ik5Bko8Ny#Hk_#uuoP`nIGCUkvs}na$sxtPpr6?y{lPM(#e2`_ z$Ohexo%6Jg%oB-)_DA#&)jocG>DDTV9bgm2vvw;E)hu-&&NKvuxdI=GRjnqhbT$?w`She8u{si?~TwC94h@7*XXR^_yBZ@1HAI);z%XLGg$yaGc zRL2rEgFv}ncJux}pDrXHivRakKRe*C$fl~xiyBM$|Jm8EwA#JCeBQf>tW4KkW<8l0 zQWUfH%8t0p9^Ib!vTcE)V7@_X(x#cThptqJc0-QU{1UdP0DJl;Le z?(2mI8$QebyDbpLKV?JKQ#C2MSq_sVUYM|Yx=jh@P5v$V#WsRi*PgR-% zl+JJp7`9B{VB!?rw7%oE$c2VA*9=OyBC-$mEev*>6`@jkUUO-uYgBnhcF&T$1;TDZ z(J!tzIyiDPYH(#9+cNXTOq0xWcVzQNhcft5v+X=hA>hW^E?!V7M!o$y}l#jC3?X8jTAik(zlRi$a3 z*dM)r*N51q*iDsxkIcJe#B6FPw~XVEK*E+CD_xZPcrRI9{;}cNLEf)-8DlJW?DPA) z{rxxAdz&>E*xh44!(z5pTK$$=Qcd@*rPJ0-?l~ghsiat2RL1h0VX^L|8wEa-}s=A;`+y|RyP7%$#ebS{f6J#n+r8o_xj3`>+eR2NKX;Hs(UE)q)g zt6Ja15$LBBKB1%NkhZeIiUY^37uR;InkH*$cFju7^WnGR7hhE6{~dexGb%JVRY_4) z%424gSTkd~%$72{EmvhO%~%naTK9hSyMxhw^V<9OzkL7W$j#k$yP4+jF(xh9%5nKc z@{gZu-=Dmy-6TBMiHmhY-myDwF{ZZ^J9Ss7`Yzqb((rK0ovX20i_JZc8CZoh3NQr; zdh{spFl(p^ws0kiEZD7n=*&*r30_K57+WR?IQ^1gm>GJ2$AD4btjgM0uIFpi@7+lL z?)D<_hswK!t8&BkU;7o1vSq&DrIpT;eh5rb#D}BQcpWwEVO?!9FVvx z_G!h`W~t77j3N(Ke|I~RZ@}PoJ3YKXp_N%Jwu3vrqPTgZOgfw8@de)&PDm@tK75X4 zLO|^9ch-x|xvIT9bF$lV!)JJ@6{#>cO=*dU;Fx&VF7@Qp3y#hLoE(dKctsT)1yufg z-KqPhfcvm{*BUn2iwTZ96>FZ)`r3B<-yJtclOLJ~mPE=KG97DJlbDh0&Uhel`3=D} z$sET8p0u8gti5Pv{LIn8+56^i&qecN8Snh4T$bjSkf+F-lI))mbZG5i;aESGO7$gg zH|8wedhIg5OP$6kE+$U>lKK=Tao3o2F->SPRhYyV^lP{3qMW|_yO)X22F(cruOHiC#(kX6@Tbt^j@7;KEDcqjw*LGXLPypLOf#KprFDIp zP>h<&r^P3XK1#%Gv@u3ozJ{N(ri|P!fw0N(8xI@6sTW+`9i)%FRX^J&2S=jr} z?&oLgK=wnq{JrZocd<*HyIB-Gn_*7hJZ1J2`x~ER3eE#WZtyopW+E!$nDFWBlBUuZ3}<@N22pZ_Rd44rYw zq$P9m_M;cs>sWvH?&bCl|0mbKZm*c|R^5(6OotTabKWmG@~rH1Zuuv*9VY|AwU?)~ z&NJ?dtkjq%DOP&q_%{28%Rikc=ax>^eYewQf$p-O|EC*iE7V18Y+kmtBC3;NLkOd% zqH6Kv$ebPNhqE`uIItA$2|cS?vnRE?s8r+)OWN1Bt#`%th+3bw7fPIPewMyilgnyD z=JuD9#UFqDT_?u==11P0{g1e8wmq}znG&WLAh>A349#0*4#(0~JY>6)8|*SW;pF8s z7nh0KbJ%aT9oiwQu)w&zWBw`OFJVdRipclpZ=4 zmBeN$ckQ^tsXF!Mc{krr{NST9(Ph=^j>%dgq8kftWpBK`E&uqUne5J9S4C_bP2R8P z6j7+%!MJ#PHe1~0=j|Kce^Y<((X{>`v-Y|J%Pt;gNP2Q8?|jLomkj)o%W}D$Thu;$ zdpBR=vC70Z->w%P(V2Z>bu9DRmOE@JQYWR(FKn3BHfyc(#ZO(=e9hkP$+TT3xqqpW z;mY(p9)-*!H`>C@Cs|oHurzn>5sz%{{3Q0}+4F>c*8GF>Zp*Ys2AAh$-HzV#M&Qro z<4G=Q{5)l1cUxE9$UiQ;;d|Nf#G41sRwNvKzoF>x-3!aQ-X+DWPs*+?%W{w7d(!A? zwC&HySvg)8wmQpaJ?mS0I77K|^J%9Ub?XFFWbA&-^%Rig^>i}Wx;Ek&&&Lm87bfrg zxNrMQ=CZonzy0O4YtP9R^jXi4O3Rn;lXbe6!0c`MpOYc{euCRP?Y}{b0VF z3#Uqmv5BW-!u8yH4Rgi!$u%eUKYkb*{`j7H-Rsr4>9Z4?SNPVwsgcOk`C8C2f#vt5 z)`qf-dn~Op%l3%>xT*J$+hS5*^yc96OwKc7r@5|J%d?2@%Y}p2m7NXmtVozCIW0Mv zb>o6EkDGm)r8}p~9=s*8^pZr|HKARHBF_dqmPv1_Pkih>CG_GoDFc}n1(7Q@Yij2h z7(I&g`EbpW|K^go28XA!yZD(HXDD>PjShU6QEisTDrqm3UmE7Py?J4JFvH;}UdL05 z{oHu(9AM^UEl$7mP^~K=P0Ph`-GtyvT!LHnEIu0*)yl1CTKYxoSz_J))rHsm{SJM6 zf1msM|Ce3|JUtXQcd~tN5=ofLD(L9dnzgfS{=Y}REnfY2Tk!Dw{zlm}dDc#w9#h!{ zC23z5fs%z2X5SJKbbPe2G5VzCXA`!B*Gd~D&E__rcJhCG_15zoPorgiaS!fZ-d)Tz z;jxuUzh4w9 z#On8WMqg_dQ$PA9p*QcE;5p6+seHERE#mRAtwGM3OkU5TEVurxKF{jB*x>#ZmBtl~ zO;Ja=tWLfER^xEw%?&eyV6il*Rm=Qp4XwJ?-`!>{l0LVN`CZimu0OAC8Gl?VzmKu{ z&(Ge9kvVzD`_S za3Rj!nWM4ulEkW1X7Mdr56&@lZ`>5Pp3xCfynro=30&0Ry>etVDM_oec^7h zb8C9&0c-xf9O?fah0g0_ad65@{rcz3q4RPJ3>jMaB+i^Y$()w;QgN~PtCKE^PdF}6 zlG%5E_lEoH{&&uv_J`&Aoj=Sq5BFS8c>nf(oAA9aoNqRLem8T&UoTI^7Jk#4mJ&Oj zezSjcJ$!!e)7SY2`K;fx=*#_A1{pD-`_sM2HyS+@d}U-d&Ocf^tJSRQc+YO7&Mc#Q zt^!BP3~n}U%q?|!=5|do?b~co)03`M=bU&ZDBVBCx-c{Mt>GTq#Gl=})+jhDrwOMk z=kNJ?cq{+)w5IPp$zidZoDJi?RR2|#ZZg%)#dp!hy*ux_ zTe}z)>3f-~_KEiTZTn%#h|Kz8|zpwWXC|2*|HoyB@ zIi~!wYQ_EMb*Or{DVF3g#P@Wqu4gE9c(3 zZ#C6!G1HVFxg$&hP7z(63sx{Nh^+_h2WnW;o|bWFLz-~QZOx?q-lZyx3al|(Kjv&P zEjnPVzI3_vq44dSm%r^O-~Ta2X507Og@4~Zf4oY%uHp8yn8w!4-;Ugu-zQjmx%kJ8 z%=ja(`G4m7%-^)(@S~+aj=#P*yC-Az$IYjexc+GHY?|ZTJ8Md`;womw!07c&DVxo* z78^xv`L=oU!bPmhBT5Vw&D|_NcWTNqlZ7h+GIlw;m!8Xe5O>1D~*SqvX8JM*UJ8~uN5@a4>Rwat8U zohujd&9#!4U3JRoRC>$u^Lu6-G5>J!;Qj6TTeA)%>xZspUGi#A;PJVN9k->w^aejS zz4oZ)?&r@8Gp9ZXyb-GEG}S1gf9=kGo@J)${eQhAvkX?t?re(AyZ_GfzsZ8_$HIqThX0wuUKs@ z3KP3FH7Fp+S4rvVZm$J=E}O4rqR|hRdBTR*@Ik>Mu1VzP6rb+OZR^T z&7XB}&cSHbcYRN8IhpeMUEFg2Mp$0&v1MVr-zLU$y#2$}@_r6;M|5&?#=!t4$CWZn zkq#mvd(ABlNHPlP?5NF3f4wZ^vcO(*O+l5T8CAb-=Fe-WxzhYy{(mWpR+XLQ0e-*S z;tLZw4jmAjc=Xz37Fpj8zqwnM@?`1P&zyhHnL)6p)A&~8FWZ`Vm(_1sm;7(JXnZ*8 zap1g=f*d20qq4#E=d4&91$VC4zIFbDU>5bPDI0h7{ycwf<-ubWtqc>B9Im-t4^|D> z$zwNp`TqHrnIjy!WU9peO)Q)9aTb^HQ3tk`S-bsAuZ0(`_C6B1zi)HfK90>RSzqNO zo27U1EZWeqwVD6n_3QIGbq~+p^vwFF-nGvS=6`IGBAh?Cuq<_26SrID{_Yn%Q&unL zXxiX8Ve6;a&DRXKT&$Cp7SnOoeg33RzQC&LR93^xUjD%7UZr1>A<8l0oPA+Ax3*}q z&bYQF_UwGi&k-T3zue(k5w81b(fs=bAMOQB{baWCVpKY-W%}IGZHh1Y0v^qnd$DK9 zxzM>6q8LuN#`0*Gh+MiDowqwvm37^|uhu_Ki1!^x-Tzl^)m5V!NcaYx)*XnH1@mdg$8fj~~o0U)i{ueR^Ezh5WW_xn8m z#u!{$Y~mzr-`=(GvNZV;Ptnw2NiIcj~3-sdUneO}~?9FE+~#kzla%&cWAUI>-c zI^DXmW~I9De4fc~^X$HQw|8{!tDHVp<1HJTa@V|e_L$GJcNc!IHCKMs6#ePIT$T%a zzXlZ?x*=vBA*so>&MI8(@!v&8m$X+Y+itQ637J|bv~|RTB`GB!-=s_y=* zppnt{%CI~tYVj}5;+~tI)sxTv6?go~_vp}R`=`M2PNnW&`}#%8(%o1)%^QzN&bqx{-x`M zu3tGr!K>Zp-0i{-w)4nGbT=uGY|NQ;ouiImqw(tMA*dk-U@}HKCp3!ns4jww@#H-4@B(q66 z|F5jx{%7qsEH?2NKleHGu3N0BCA&Aw>BrFq7ytE_9CKYXm>r^YXMcI|e`ddK_CvAv zs-iQ(IbQwyHr;RMt2MIs_)NEo?|7Xwqkmqdg!S@;N|SEq?(e<3p^mw)?!C>PUkAef zE8qM1GJd}OQ!bymEGtsD78&$sEzRPNo#>Oi*Ua~<JjWaBE=T0Y$boOhujqKOrns-SayX_bK;g2W-LwPLQ6)xr(cWhZ# z`W-ae5NpdC)EF?|>W_0w*G5IttBw2rv5B5=S?SP`UAOg;q+apXpyPMunIFtr)9{(K zGkdm7-t2|%Be*X_Y*A$S`E+H9V$0Dxtd}=jmNVW^_C~Dc;T!YE-syYASF=rXPU(KT z#<=3g#{G|-m&-S=pYuDfN3}ok`rcc~GavvPvYnAJE z4K6>tX65Y%U7y+i{WN;^C11WbditG)x8iSFYSwWmn*4kJ`upP@!ReE?Dl`h*Qt2^B z44=vKT1HIwh8Xv@nff`C${jPx3O7~NTSl(!krs3^V|lH+uJE3Yq>OznXH#}`{Q;q^ zQC(NH%^S0W4R)>bFBCEl?xYs@voP;@!{}W@$+GsD>I7j@>#z#lwM-O6na3e+rH!DDTc6h zyBR%$oc69Mym)>3qr>-aNZbGT^Y{P%(kGttZ-_RYQoQNuj|oA&-{CP$EEVi@v9Y|tA+0^34Oq{@{gC>#;ZZ9b#40`xgY0m zW$PAgV>{jPnr-v5uSqp)ns(@1Ft+4Z%-!ZRY1V7LS=Te9n+xyQdAy$PpSjj?j=M@a zlUn+zr<g zd#YXb8buE_E++;4ZHMx8-`x84>-o05b2l4)e71}6lY27X3H@|YfglGC)}<;fAvc4< zpQc-HW)*#K;Co7BJ~q9PnF|E~S*+f_AZ)3xMH>(ghSJ@ZQ|_sbs> z&oaMBArg-x3K|zB7&%?#eZ=ubWQOPg#yMwMj&hyNnx)FXn#8(@VUm*otAJ9=0*;`z z04;V;!6iKk8cd4pPArQ!jwl{byrF5a{K1w6Q?55tnuEh`M@U5rD*TNQXjA9qUHh|n zf!xOPcI`W|z8c=yv8HA5w;%d{9{q`qGnu*fMYQ<+qQ8eLzP>JZnRG>fO>kR}-N_)Y zi?K5d%buzKFZj9o{)W>HrfJj6PRisaOXND5^#}MlEm3pq)Z;K|5v_O_JyX+>(XUbC z%lsKGiY*-q6Eqzv7IwEC5Nx#gWPiT#|Lk+xjW_CVwM}t!o1g7?$ySo>AZzgNw0S|R zdhEp5Sa-f{VtuDAzfY`vZ>n9{oB6EWhBh_lA9Sm&pZ?}E^Er#(5*E)5;^O#^iv_Fe z3NrICoj$K{puE-?JoCTuZ&&hnbRrv*p5SSPnzU3rhcD%@n+t|bCC@? z5_~RCI{y4p%i3n^x^12#hf{Ll_MGgs+lm9k&Sgr>R=66vE=>E$oPFL}|1=x_iyTk8 ztY`hU^S6A2QvIJ^_cx*Cb6TR`OB`^?l$l=l{OJD>)t^C{56($Xe;oRMqbpn%r?fu8uGu8a`-LjjyQCokl^gHo)-s#v6*6*nI zirE~qUdl^&e?)W3{S@U6l_n1*9@Z&AC!T(uz;(xZXGOihDo)3&NdiXtEF0Hrio8Ce zyXvYIYqm_#T(j*4e!|Tvo)gv7ZhtuJlzq5%?`O*?tQ#{6jtW-G7=7NxtDU^D;JBXc zh8pFxbMyHW|9oQ=ug!hwtu}x9ALEpLUthld(0qSR&;P$C))yYk_h)5j34GDyQLt~t z0ujX?mXdAu&7OZ(c*Yd3m~MHyZJr>jSYgoSU266lzRc_WE?*;a=#WSO$At$hKa7w6 zzrSSl`u}Io_S^n^u#V+ri^9W)>prbnF=@i3#mfwYCNL`Ybg&f9y0zn${o3<-yUSJG z%>53%Ti+RQZ-(=vEAc@O?X^WeE;?p8b*)gMz3jG(bN{v~EDBl}z3{@?3sOTt@|dCUw*pI_Rwza1+u}Sl=P`@ufuH{$Gu=AKq9Pr)cx_x~0YUFS`p5PM5Da znfiHC?VQ|ejL8fy-rv5b<*LTh`n=f0S*uZ@<^Dshv==RjCJCC+XRc?=5_o*$`kQX{ z_dA-u#nmv>|NSj5a$N4aTKWzbl^rLQIS=}39oqj~`+b`6^grJ$H@IxFES}YyE`RoP6=MW@W|CV%yC7ki}dL=N@0%2>fD$T{S!6M1hKhEtQQL`T{OYfqp+GOqtH%l(qTMwFd{j+Z9VU z9q^a@;{SHD)1eg(DbbUD&fU~>;(xCjNB1YDz$fdMGyON8UA%#XR4wiY{YvI8-2bIEvHk%0-9FZPQPhx%ql{DcP&fimZNO{`d2j zmW;Ujmh5Vc3KI%4pRWsZ+RJ3iz_9PbJn5+0ev|TBwm&jW{djBtsx!(>4FYX;F)Ryn zE}mvslB1(ie)FGm)6^giC;4-W=D1ti-ATD8z{R5YQ`d-@aiz;Peuk|@4Hd0g4KrtF zbR2xA%LKhePYUsIZ0Y6OAr61kL{O|pAt%l$xu&HvM4xwlvIjoDjf3T>`Y4n2L@}#l5$Ii^`f2e=`BvF?f>DgZmo27rV@65?gzWqX&d6F{Ar9#uV zi3$u7t_)m3g8RR&kX6}S9JI=UL&VvF&xuo%V}(Q2ty!Dy`pYz`R_jk@QVG1S_R;## zgvU`^f1Ucmu!G^}OUs%!Z(5leu69OxPGCv89%1UV;9P%^0#k?L)F&3QrME(_iGAAZ z%%d><`){_|2Q9{in(U*i!dE{@ z-M{I-V`6k%A=irQJaxYg8($RFT@*gwWrl=roy>L3R&G^`E$b|kUxvF+3YehPGR1Ov z%0-LgPHewwBnx7%JojGfy(XlA`JUT}a@KF(&1=O2?RF`|bC{KiRyQXt^k+}(o7yGN zX4#S`7Z~EAcfD03ykzGJ>8~k0Le?dWoIVoSM|Qajy_@@ut92p6g{kwq7yJ?1efLjj z(j}2Qc3Yb44)+vS?@m84CrHolG@oCyhhq?nqOziBOPTP(Nhgo~{rjm#sEbAOx^-*7 z+G&M19NU{C3U-<)Z3)Rs}gjwdO@kw-9Pt8YHkJLAN_{8@|p`a1LjQwLTduuSOU{<}{e0Rk!n^3s-O0_H&&2L*^>}2p=Ca3S ztFSc{S5&KFJT&u!9IG;P@0Mgv>I*+)pudZ&c9XWf>$5|7o&v{q{g6|ClEe^-qQLh|s?H}1q=RJdn+MA6SSg_=xAkj-&^;bW4!_4Dlhx0=?lkF%{9K)Q zSHjKhSm3(61A8p1y(Lx5(#4Z*=*+wwJ*Pat&b)Hrq@5=s_il`m-~IlhskdC*jrs;( zEkBD@yEiMb3{H9$nym#MC}7V9BbW)_{ot{DO}TEOb)%c=p_mZDBt* zemVPl?)Q6V-kd2ueA8oo{XeF^hWq#aJ+v|C?5^)8=N|QbZ?~tm=F_3W``zj))LpMA zE$Ur5voL6XzwV-Rk5|PF3k6jjvKO$u(0XC;!hJ!(QuhV(RXgjhb)Gx)&~gFW9=;gX zJf`=I@r?T!`WrKU>}T83=->FhvHF0$!!;Ex#`K2t#?J?MJ}3n+uVcF2@cMw20K@D9 zVg=Gagnmf<;MXz$W4qt)Avo_{PFP7x>We z=ycl&L$6(3^)XK?qMn{)o;7Rz!PhD$XL(#zU3o?NgT~4z&!fts{UJ6%TPLrY8M0?1 zp2H%HakSJwr%+c*5Rdb3XW|7;bPUwvLYEhgP=QPh$D8K-PBo*G?T z`Spvv?Zx0F{t8_k#n-2p{&X#rbJy5-wA=H{i6HxD=j;sMzhC^nRQ~V#-!J5ketA^K z`>!nNQ@&xPxte|7$y*FpPwsh9WL`6~XtV6~>@z?AEZbd_*IO#7nms-Je$~`1th3e_ z-^=)&xA;)h+Ec35CA;%>L@w=Aud-iprmW&{@v4$#dB4wgZ9X_l_x#EmtNZr8`=pRE z?|9Vfsf{|JR@Dn$i$42Qx9(HpzV1}9wzpby?T)Ozn6~}KCXLTm6z9p^{$l;`VV&cR zGRtd4pAzYkM4y+mJ^q4d(dWjvd6)j6KoZW9XN zYOihHyv(`nMvT{2vwov>wTF{s`$D&!oMP?jChU2lLv2Zo-Sx7F7Z)lH?D9)?e8aMR zuDSEV(`;*jv3Kle1v{?6z;c6Hc^jNp>^en$UQ^w_@)ErvD37f4C%a{1Lw>J%#Q2 zTGMUE_rCrbX?rc3{deyA=V~kOmsi*Ne7Wc1ck9US1)`z-4h)+$zCMz@US!n%BDyN@ zndn}tg?|=*->C7p%iJV#N~P=j1&hw8R&{Cw1^*ZP#~$F#&VgJefDVF&lorfk%bjxX z$;)IhGB7M)Vqg$qU|{fd4RO@<^mEhqb@cOea}5sB^L6XJa5}HaL8Rf~-p=`8pY&b~pdAR{69g8y{*Mw{=YV{h;i9B+Dm*PF5bKL+KK> z7AYG_X7@JMbiFc^-fFfo^WeWF)0+Z+AKNKsv-jKIhXvD2{mp-i{NADH*PTbK? z0q6Vgs?YpUx>V0ET{PLZ#96rgww1sapXVn^0uH}4{wiOe9xZw@%zb{+zM|jB*ID8d z*RH=7Td?lV>O?2o6BQ0KzdJ~4N}JRkGKb1-aNZ?lGS3=N;1Lo4Wd7uiEd|@n5Au zafKWssBu+fndZ{X$iQ%wiGe`?Ij)>tgF^Iz-Fh$P=Pxo~IR5du+CFx zU%+>;aK&WCNAq-+>P9@Bugmj%_5Dq6C&^lFuD)jSQhk5^uLGBuuJSJT^4OV>YB;ft z`NI3VMO|55Q(m6_-TH-DtYvyn%-3rEVv7);X+eS7+#wsDyfv73OJgc?Ne9nup*L27 z2V|A=r+xjLvO=;-b9U5OL2b6>Hov*&cuZrI?x;O?eBHL1EgnHX zzuqg3PPtzyb11O;QIFPg*_AU+eF$!w#q?f7KE(A_;`4KAD)(|rR5G95+V--)sW|5i z-|??+xPMFjT>eV_^PK0_mpywll_Sh1UC0irnKJv8WZo_B&!663|9j=naiNn^YqouF zUwh}6rMw$Y*CmPgp#O^{r_9@V_KR7>Q-wI0<0d7t(s$)t0Qq*1>&8u}Fwr z&~90Fw%q2IXD(fO8>9OzV)j9OmU9yX@);H`Kll4Y?wqrq1x`HlU8C=@iDj49-eR+_ zP2Pz&zb9Tl=oS52JDvGk!IM`fJ-0g-=>Oh-(fx)tC?b)g3pFD3Z!J)YXJTOBU}j(t z#E8hCU`OMOq_o7xSF%25{62U3#JMdCI$y4K6u#`P=H}*{$LwbomTQ-$rWwY@oY$gs zjMcYCpLdqTe5Q@}nbOqU)YPUO;G3++9JfQtEo^~U@dnN_3w3f1`7Aux#>7^mz&G7s zcXFIpU|m|*!WoSm9xahG#I+VZm^3S7R-S_Tx$KObgsgDBu1PUB+GJ#4{>@0jq|C_F%*f~UC*zb~Wha-`&NcqL=4kD@_`m(9f_%IoUa=PvtWm8|&l$sw)P4xM)zvYww`$bN6x{8>jQT-)<7VuI7_b$|Mv+@I8b z=I}hbx=B-S?ezV>{MX9u&yH=c+8Xw^IDG#@)quB~Vsei@zAUV^?76G#%Nu$dufGZE z`x&<{zTW)Pul!vnj+{AhphK+Xj6=EU`u$gm{;j!Ke$-8#@o~jmUG{#9SWX z7N1vkjW#mKG5Xy+_n6Y@+a`Ut{%c&@X#IP?)q{zT)&^VsbKe(zFFGgW%G}q2YROVr z(R=niY$;ipdaqLNe#yGSRqvO&oVhzG%u4Rx+8;L6-t*Qc9?bsvcKZ+6PcqEgmy{Id z1?=7Oc188}HHnXfn~$|0_>z#|Uv|LxGJ8;wQ!m@JV{DIE+j5iaxSz$nz1nprY^qV_ z+E-fp+S#r0EA)T=`0=%w+rL+R?@bw{O~2QKb)uAW<$ z^KLtrv%kOHuJre6TW7ypEgeyRGUbNsxelFqU!Fbtm)&PqQ17j+@&~?NEMu zQoQf(+Lc`Ua`*o2pBftWBsFjH-2Duo)QOx11H2iTL>Lg2!b{1LvzCku3_F+@7=#&E zKy6JVT$Ea@pOaXbUs|G{o0yrWSCO0Z?-T5OU%<-10K$A=Q5bQBnE`YlY<_7`a%!<& zVo`B2y522vUGXoK85lrV5vC1HFW?00Mcj6P?H~zs= 21 )); then + SU_CMD="su 0" + fi + fi + + if [ $# -eq 0 ] ; then + # If nothing specified, print the command line (stripping off "chrome ") + adb shell "cat $CMD_LINE_FILE 2>/dev/null" | cut -d ' ' -s -f2- + elif [ $# -eq 1 ] && [ "$1" = '' ] ; then + # If given an empty string, delete the command line. + set -x + adb shell $SU_CMD rm $CMD_LINE_FILE >/dev/null + else + # Else set it. + set -x + adb shell "echo 'chrome $*' | $SU_CMD dd of=$CMD_LINE_FILE" + # Prevent other apps from modifying flags (this can create security issues). + adb shell $SU_CMD chmod 0664 $CMD_LINE_FILE + fi +} + diff --git a/engine/src/flutter/build/android/adb_content_shell_command_line b/engine/src/flutter/build/android/adb_content_shell_command_line new file mode 100755 index 0000000000..2ac7ece75c --- /dev/null +++ b/engine/src/flutter/build/android/adb_content_shell_command_line @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright (c) 2012 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. + +# If no flags are given, prints the current content shell flags. +# +# Otherwise, the given flags are used to REPLACE (not modify) the content shell +# flags. For example: +# adb_content_shell_command_line --enable-webgl +# +# To remove all content shell flags, pass an empty string for the flags: +# adb_content_shell_command_line "" + +. $(dirname $0)/adb_command_line_functions.sh +CMD_LINE_FILE=/data/local/tmp/content-shell-command-line +REQUIRES_SU=0 +set_command_line "$@" + diff --git a/engine/src/flutter/build/android/adb_device_functions.sh b/engine/src/flutter/build/android/adb_device_functions.sh new file mode 100755 index 0000000000..66cc32fc4e --- /dev/null +++ b/engine/src/flutter/build/android/adb_device_functions.sh @@ -0,0 +1,139 @@ +#!/bin/bash +# +# Copyright (c) 2012 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 collection of functions useful for maintaining android devices + + +# Run an adb command on all connected device in parallel. +# Usage: adb_all command line to eval. Quoting is optional. +# +# Examples: +# adb_all install Chrome.apk +# adb_all 'shell cat /path/to/file' +# +adb_all() { + if [[ $# == 0 ]]; then + echo "Usage: adb_all . Quoting is optional." + echo "Example: adb_all install Chrome.apk" + return 1 + fi + local DEVICES=$(adb_get_devices -b) + local NUM_DEVICES=$(echo $DEVICES | wc -w) + if (( $NUM_DEVICES > 1 )); then + echo "Looping over $NUM_DEVICES devices" + fi + _adb_multi "$DEVICES" "$*" +} + + +# Run a command on each connected device. Quoting the command is suggested but +# not required. The script setups up variable DEVICE to correspond to the +# current serial number. Intended for complex one_liners that don't work in +# adb_all +# Usage: adb_device_loop 'command line to eval' +adb_device_loop() { + if [[ $# == 0 ]]; then + echo "Intended for more complex one-liners that cannot be done with" \ + "adb_all." + echo 'Usage: adb_device_loop "echo $DEVICE: $(adb root &&' \ + 'adb shell cat /data/local.prop)"' + return 1 + fi + local DEVICES=$(adb_get_devices) + if [[ -z $DEVICES ]]; then + return + fi + # Do not change DEVICE variable name - part of api + for DEVICE in $DEVICES; do + DEV_TYPE=$(adb -s $DEVICE shell getprop ro.product.device | sed 's/\r//') + echo "Running on $DEVICE ($DEV_TYPE)" + ANDROID_SERIAL=$DEVICE eval "$*" + done +} + +# Erases data from any devices visible on adb. To preserve a device, +# disconnect it or: +# 1) Reboot it into fastboot with 'adb reboot bootloader' +# 2) Run wipe_all_devices to wipe remaining devices +# 3) Restore device it with 'fastboot reboot' +# +# Usage: wipe_all_devices [-f] +# +wipe_all_devices() { + if [[ -z $(which adb) || -z $(which fastboot) ]]; then + echo "aborting: adb and fastboot not in path" + return 1 + elif ! $(groups | grep -q 'plugdev'); then + echo "If fastboot fails, run: 'sudo adduser $(whoami) plugdev'" + fi + + local DEVICES=$(adb_get_devices -b) + + if [[ $1 != '-f' ]]; then + echo "This will ERASE ALL DATA from $(echo $DEVICES | wc -w) device." + read -p "Hit enter to continue" + fi + + _adb_multi "$DEVICES" "reboot bootloader" + # Subshell to isolate job list + ( + for DEVICE in $DEVICES; do + fastboot_erase $DEVICE & + done + wait + ) + + # Reboot devices together + for DEVICE in $DEVICES; do + fastboot -s $DEVICE reboot + done +} + +# Wipe a device in fastboot. +# Usage fastboot_erase [serial] +fastboot_erase() { + if [[ -n $1 ]]; then + echo "Wiping $1" + local SERIAL="-s $1" + else + if [ -z $(fastboot devices) ]; then + echo "No devices in fastboot, aborting." + echo "Check out wipe_all_devices to see if sufficient" + echo "You can put a device in fastboot using adb reboot bootloader" + return 1 + fi + local SERIAL="" + fi + fastboot $SERIAL erase cache + fastboot $SERIAL erase userdata +} + +# Get list of devices connected via adb +# Args: -b block until adb detects a device +adb_get_devices() { + local DEVICES="$(adb devices | grep 'device$')" + if [[ -z $DEVICES && $1 == '-b' ]]; then + echo '- waiting for device -' >&2 + local DEVICES="$(adb wait-for-device devices | grep 'device$')" + fi + echo "$DEVICES" | awk -vORS=' ' '{print $1}' | sed 's/ $/\n/' +} + +################################################### +## HELPER FUNCTIONS +################################################### + +# Run an adb command in parallel over a device list +_adb_multi() { + local DEVICES=$1 + local ADB_ARGS=$2 + ( + for DEVICE in $DEVICES; do + adb -s $DEVICE $ADB_ARGS & + done + wait + ) +} diff --git a/engine/src/flutter/build/android/adb_gdb b/engine/src/flutter/build/android/adb_gdb new file mode 100755 index 0000000000..65ec7b20b8 --- /dev/null +++ b/engine/src/flutter/build/android/adb_gdb @@ -0,0 +1,1047 @@ +#!/bin/bash +# +# Copyright (c) 2012 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 generic script used to attach to a running Chromium process and +# debug it. Most users should not use this directly, but one of the +# wrapper scripts like adb_gdb_content_shell +# +# Use --help to print full usage instructions. +# + +PROGNAME=$(basename "$0") +PROGDIR=$(dirname "$0") + +# Location of Chromium-top-level sources. +CHROMIUM_SRC=$(cd "$PROGDIR"/../.. >/dev/null && pwd 2>/dev/null) + +# Location of Chromium out/ directory. +if [ -z "$CHROMIUM_OUT_DIR" ]; then + CHROMIUM_OUT_DIR=out +fi + +TMPDIR= +GDBSERVER_PIDFILE= +TARGET_GDBSERVER= +COMMAND_PREFIX= + +clean_exit () { + if [ "$TMPDIR" ]; then + GDBSERVER_PID=$(cat $GDBSERVER_PIDFILE 2>/dev/null) + if [ "$GDBSERVER_PID" ]; then + log "Killing background gdbserver process: $GDBSERVER_PID" + kill -9 $GDBSERVER_PID >/dev/null 2>&1 + fi + if [ "$TARGET_GDBSERVER" ]; then + log "Removing target gdbserver binary: $TARGET_GDBSERVER." + "$ADB" shell "$COMMAND_PREFIX" rm "$TARGET_GDBSERVER" >/dev/null 2>&1 + fi + log "Cleaning up: $TMPDIR" + rm -rf "$TMPDIR" + fi + trap "" EXIT + exit $1 +} + +# Ensure clean exit on Ctrl-C or normal exit. +trap "clean_exit 1" INT HUP QUIT TERM +trap "clean_exit \$?" EXIT + +panic () { + echo "ERROR: $@" >&2 + exit 1 +} + +fail_panic () { + if [ $? != 0 ]; then panic "$@"; fi +} + +log () { + if [ "$VERBOSE" -gt 0 ]; then + echo "$@" + fi +} + +DEFAULT_PULL_LIBS_DIR=/tmp/$USER-adb-gdb-libs + +# NOTE: Allow wrapper scripts to set various default through ADB_GDB_XXX +# environment variables. This is only for cosmetic reasons, i.e. to +# display proper + +# Allow wrapper scripts to set the default activity through +# the ADB_GDB_ACTIVITY variable. Users are still able to change the +# final activity name through --activity= option. +# +# This is only for cosmetic reasons, i.e. to display the proper default +# in the --help output. +# +DEFAULT_ACTIVITY=${ADB_GDB_ACTIVITY:-".Main"} + +# Allow wrapper scripts to set the program name through ADB_GDB_PROGNAME +PROGNAME=${ADB_GDB_PROGNAME:-$(basename "$0")} + +ACTIVITY=$DEFAULT_ACTIVITY +ADB= +ANNOTATE= +# Note: Ignore BUILDTYPE variable, because the Ninja build doesn't use it. +BUILDTYPE= +FORCE= +GDBEXEPOSTFIX=gdb +GDBINIT= +GDBSERVER= +HELP= +NDK_DIR= +NO_PULL_LIBS= +PACKAGE_NAME= +PID= +PORT= +PRIVILEGED= +PRIVILEGED_INDEX= +PROGRAM_NAME="activity" +PULL_LIBS= +PULL_LIBS_DIR= +SANDBOXED= +SANDBOXED_INDEX= +START= +SU_PREFIX= +SYMBOL_DIR= +TARGET_ARCH= +TOOLCHAIN= +VERBOSE=0 + +for opt; do + optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') + case $opt in + --adb=*) + ADB=$optarg + ;; + --activity=*) + ACTIVITY=$optarg + ;; + --annotate=3) + ANNOTATE=$optarg + ;; + --force) + FORCE=true + ;; + --gdbserver=*) + GDBSERVER=$optarg + ;; + --gdb=*) + GDB=$optarg + ;; + --help|-h|-?) + HELP=true + ;; + --ndk-dir=*) + NDK_DIR=$optarg + ;; + --no-pull-libs) + NO_PULL_LIBS=true + ;; + --package-name=*) + PACKAGE_NAME=$optarg + ;; + --pid=*) + PID=$optarg + ;; + --port=*) + PORT=$optarg + ;; + --privileged) + PRIVILEGED=true + ;; + --privileged=*) + PRIVILEGED=true + PRIVILEGED_INDEX=$optarg + ;; + --program-name=*) + PROGRAM_NAME=$optarg + ;; + --pull-libs) + PULL_LIBS=true + ;; + --pull-libs-dir=*) + PULL_LIBS_DIR=$optarg + ;; + --sandboxed) + SANDBOXED=true + ;; + --sandboxed=*) + SANDBOXED=true + SANDBOXED_INDEX=$optarg + ;; + --script=*) + GDBINIT=$optarg + ;; + --start) + START=true + ;; + --su-prefix=*) + SU_PREFIX=$optarg + ;; + --symbol-dir=*) + SYMBOL_DIR=$optarg + ;; + --out-dir=*) + CHROMIUM_OUT_DIR=$optarg + ;; + --target-arch=*) + TARGET_ARCH=$optarg + ;; + --toolchain=*) + TOOLCHAIN=$optarg + ;; + --ui) + GDBEXEPOSTFIX=gdbtui + ;; + --verbose) + VERBOSE=$(( $VERBOSE + 1 )) + ;; + --debug) + BUILDTYPE=Debug + ;; + --release) + BUILDTYPE=Release + ;; + -*) + panic "Unknown option $OPT, see --help." >&2 + ;; + *) + if [ "$PACKAGE_NAME" ]; then + panic "You can only provide a single package name as argument!\ + See --help." + fi + PACKAGE_NAME=$opt + ;; + esac +done + +print_help_options () { + cat <] + +Attach gdb to a running Android $PROGRAM_NAME process. + +If provided, must be the name of the Android application's +package name to be debugged. You can also use --package-name= to +specify it. +EOF + fi + + cat < option) or a privileged (--privileged or +--privileged=) service. + +This script needs several things to work properly. It will try to pick +them up automatically for you though: + + - target gdbserver binary + - host gdb client (e.g. arm-linux-androideabi-gdb) + - directory with symbolic version of $PROGRAM_NAME's shared libraries. + +You can also use --ndk-dir= to specify an alternative NDK installation +directory. + +The script tries to find the most recent version of the debug version of +shared libraries under one of the following directories: + + \$CHROMIUM_SRC//Release/lib/ (used by Ninja builds) + \$CHROMIUM_SRC//Debug/lib/ (used by Ninja builds) + \$CHROMIUM_SRC//Release/lib.target/ (used by Make builds) + \$CHROMIUM_SRC//Debug/lib.target/ (used by Make builds) + +Where is 'out' by default, unless the --out= option is used or +the CHROMIUM_OUT_DIR environment variable is defined. + +You can restrict this search by using --release or --debug to specify the +build type, or simply use --symbol-dir= to specify the file manually. + +The script tries to extract the target architecture from your target device, +but if this fails, will default to 'arm'. Use --target-arch= to force +its value. + +Otherwise, the script will complain, but you can use the --gdbserver, +--gdb and --symbol-lib options to specify everything manually. + +An alternative to --gdb= is to use --toollchain= to specify +the path to the host target-specific cross-toolchain. + +You will also need the 'adb' tool in your path. Otherwise, use the --adb +option. The script will complain if there is more than one device connected +and ANDROID_SERIAL is not defined. + +The first time you use it on a device, the script will pull many system +libraries required by the process into a temporary directory. This +is done to strongly improve the debugging experience, like allowing +readable thread stacks and more. The libraries are copied to the following +directory by default: + + $DEFAULT_PULL_LIBS_DIR/ + +But you can use the --pull-libs-dir= option to specify an +alternative. The script can detect when you change the connected device, +and will re-pull the libraries only in this case. You can however force it +with the --pull-libs option. + +Any local .gdbinit script will be ignored, but it is possible to pass a +gdb command script with the --script= option. Note that its commands +will be passed to gdb after the remote connection and library symbol +loading have completed. + +Valid options: + --help|-h|-? Print this message. + --verbose Increase verbosity. + + --sandboxed Debug first sandboxed process we find. + --sandboxed= Debug specific sandboxed process. + --symbol-dir= Specify directory with symbol shared libraries. + --out-dir= Specify the out directory. + --package-name= Specify package name (alternative to 1st argument). + --privileged Debug first privileged process we find. + --privileged= Debug specific privileged process. + --program-name= Specify program name (cosmetic only). + --pid= Specify application process pid. + --force Kill any previous debugging session, if any. + --start Start package's activity on device. + --ui Use gdbtui instead of gdb + --activity= Activity name for --start [$DEFAULT_ACTIVITY]. + --annotate= Enable gdb annotation. + --script= Specify extra GDB init script. + + --gdbserver= Specify target gdbserver binary. + --gdb= Specify host gdb client binary. + --target-arch= Specify NDK target arch. + --adb= Specify host ADB binary. + --port= Specify the tcp port to use. + + --su-prefix= Prepend to 'adb shell' commands that are + run by this script. This can be useful to use + the 'su' program on rooted production devices. + e.g. --su-prefix="su -c" + + --pull-libs Force system libraries extraction. + --no-pull-libs Do not extract any system library. + --libs-dir= Specify system libraries extraction directory. + + --debug Use libraries under out/Debug. + --release Use libraries under out/Release. + +EOF + exit 0 +fi + +if [ -z "$PACKAGE_NAME" ]; then + panic "Please specify a package name on the command line. See --help." +fi + +if [ -z "$NDK_DIR" ]; then + ANDROID_NDK_ROOT=$(PYTHONPATH=$CHROMIUM_SRC/build/android python -c \ +'from pylib.constants import ANDROID_NDK_ROOT; print ANDROID_NDK_ROOT,') +else + if [ ! -d "$NDK_DIR" ]; then + panic "Invalid directory: $NDK_DIR" + fi + if [ ! -f "$NDK_DIR/ndk-build" ]; then + panic "Not a valid NDK directory: $NDK_DIR" + fi + ANDROID_NDK_ROOT=$NDK_DIR +fi + +if [ "$GDBINIT" -a ! -f "$GDBINIT" ]; then + panic "Unknown --script file: $GDBINIT" +fi + +# Check that ADB is in our path +if [ -z "$ADB" ]; then + ADB=$(which adb 2>/dev/null) + if [ -z "$ADB" ]; then + panic "Can't find 'adb' tool in your path. Install it or use \ +--adb=" + fi + log "Auto-config: --adb=$ADB" +fi + +# Check that it works minimally +ADB_VERSION=$($ADB version 2>/dev/null) +echo "$ADB_VERSION" | fgrep -q -e "Android Debug Bridge" +if [ $? != 0 ]; then + panic "Your 'adb' tool seems invalid, use --adb= to specify a \ +different one: $ADB" +fi + +# If there are more than one device connected, and ANDROID_SERIAL is not +# defined, print an error message. +NUM_DEVICES_PLUS2=$($ADB devices 2>/dev/null | wc -l) +if [ "$NUM_DEVICES_PLUS2" -lt 3 -a -z "$ANDROID_SERIAL" ]; then + echo "ERROR: There is more than one Android device connected to ADB." + echo "Please define ANDROID_SERIAL to specify which one to use." + exit 1 +fi + +# Run a command through adb shell, strip the extra \r from the output +# and return the correct status code to detect failures. This assumes +# that the adb shell command prints a final \n to stdout. +# $1+: command to run +# Out: command's stdout +# Return: command's status +# Note: the command's stderr is lost +adb_shell () { + local TMPOUT="$(mktemp)" + local LASTLINE RET + local ADB=${ADB:-adb} + + # The weird sed rule is to strip the final \r on each output line + # Since 'adb shell' never returns the command's proper exit/status code, + # we force it to print it as '%%' in the temporary output file, + # which we will later strip from it. + $ADB shell $@ ";" echo "%%\$?" 2>/dev/null | \ + sed -e 's![[:cntrl:]]!!g' > $TMPOUT + # Get last line in log, which contains the exit code from the command + LASTLINE=$(sed -e '$!d' $TMPOUT) + # Extract the status code from the end of the line, which must + # be '%%'. + RET=$(echo "$LASTLINE" | \ + awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,RSTART+2); } }') + # Remove the status code from the last line. Note that this may result + # in an empty line. + LASTLINE=$(echo "$LASTLINE" | \ + awk '{ if (match($0, "%%[0-9]+$")) { print substr($0,1,RSTART-1); } }') + # The output itself: all lines except the status code. + sed -e '$d' $TMPOUT && printf "%s" "$LASTLINE" + # Remove temp file. + rm -f $TMPOUT + # Exit with the appropriate status. + return $RET +} + +# Find the target architecture from the target device. +# This returns an NDK-compatible architecture name. +# out: NDK Architecture name, or empty string. +get_gyp_target_arch () { + local ARCH=$(adb_shell getprop ro.product.cpu.abi) + case $ARCH in + mips|x86|x86_64) echo "$ARCH";; + arm64*) echo "arm64";; + arm*) echo "arm";; + *) echo ""; + esac +} + +if [ -z "$TARGET_ARCH" ]; then + TARGET_ARCH=$(get_gyp_target_arch) + if [ -z "$TARGET_ARCH" ]; then + TARGET_ARCH=arm + fi +else + # Nit: accept Chromium's 'ia32' as a valid target architecture. This + # script prefers the NDK 'x86' name instead because it uses it to find + # NDK-specific files (host gdb) with it. + if [ "$TARGET_ARCH" = "ia32" ]; then + TARGET_ARCH=x86 + log "Auto-config: --arch=$TARGET_ARCH (equivalent to ia32)" + fi +fi + +# Detect the NDK system name, i.e. the name used to identify the host. +# out: NDK system name (e.g. 'linux' or 'darwin') +get_ndk_host_system () { + local HOST_OS + if [ -z "$NDK_HOST_SYSTEM" ]; then + HOST_OS=$(uname -s) + case $HOST_OS in + Linux) NDK_HOST_SYSTEM=linux;; + Darwin) NDK_HOST_SYSTEM=darwin;; + *) panic "You can't run this script on this system: $HOST_OS";; + esac + fi + echo "$NDK_HOST_SYSTEM" +} + +# Detect the NDK host architecture name. +# out: NDK arch name (e.g. 'x86' or 'x86_64') +get_ndk_host_arch () { + local HOST_ARCH HOST_OS + if [ -z "$NDK_HOST_ARCH" ]; then + HOST_OS=$(get_ndk_host_system) + HOST_ARCH=$(uname -p) + case $HOST_ARCH in + i?86) NDK_HOST_ARCH=x86;; + x86_64|amd64) NDK_HOST_ARCH=x86_64;; + *) panic "You can't run this script on this host architecture: $HOST_ARCH";; + esac + # Darwin trick: "uname -p" always returns i386 on 64-bit installations. + if [ "$HOST_OS" = darwin -a "$NDK_HOST_ARCH" = "x86" ]; then + # Use '/usr/bin/file', not just 'file' to avoid buggy MacPorts + # implementations of the tool. See http://b.android.com/53769 + HOST_64BITS=$(/usr/bin/file -L "$SHELL" | grep -e "x86[_-]64") + if [ "$HOST_64BITS" ]; then + NDK_HOST_ARCH=x86_64 + fi + fi + fi + echo "$NDK_HOST_ARCH" +} + +# Convert an NDK architecture name into a GNU configure triplet. +# $1: NDK architecture name (e.g. 'arm') +# Out: Android GNU configure triplet (e.g. 'arm-linux-androideabi') +get_arch_gnu_config () { + case $1 in + arm) + echo "arm-linux-androideabi" + ;; + arm64) + echo "aarch64-linux-android" + ;; + x86) + echo "i686-linux-android" + ;; + x86_64) + echo "x86_64-linux-android" + ;; + mips) + echo "mipsel-linux-android" + ;; + *) + echo "$ARCH-linux-android" + ;; + esac +} + +# Convert an NDK architecture name into a toolchain name prefix +# $1: NDK architecture name (e.g. 'arm') +# Out: NDK toolchain name prefix (e.g. 'arm-linux-androideabi') +get_arch_toolchain_prefix () { + # Return the configure triplet, except for x86! + if [ "$1" = "x86" ]; then + echo "$1" + else + get_arch_gnu_config $1 + fi +} + +# Find a NDK toolchain prebuilt file or sub-directory. +# This will probe the various arch-specific toolchain directories +# in the NDK for the needed file. +# $1: NDK install path +# $2: NDK architecture name +# $3: prebuilt sub-path to look for. +# Out: file path, or empty if none is found. +get_ndk_toolchain_prebuilt () { + local NDK_DIR="${1%/}" + local ARCH="$2" + local SUBPATH="$3" + local NAME="$(get_arch_toolchain_prefix $ARCH)" + local FILE TARGET + FILE=$NDK_DIR/toolchains/$NAME-4.9/prebuilt/$SUBPATH + if [ ! -f "$FILE" ]; then + FILE=$NDK_DIR/toolchains/$NAME-4.8/prebuilt/$SUBPATH + if [ ! -f "$FILE" ]; then + FILE= + fi + fi + echo "$FILE" +} + +# Find the path to an NDK's toolchain full prefix for a given architecture +# $1: NDK install path +# $2: NDK target architecture name +# Out: install path + binary prefix (e.g. +# ".../path/to/bin/arm-linux-androideabi-") +get_ndk_toolchain_fullprefix () { + local NDK_DIR="$1" + local ARCH="$2" + local TARGET NAME HOST_OS HOST_ARCH GCC CONFIG + + # NOTE: This will need to be updated if the NDK changes the names or moves + # the location of its prebuilt toolchains. + # + GCC= + HOST_OS=$(get_ndk_host_system) + HOST_ARCH=$(get_ndk_host_arch) + CONFIG=$(get_arch_gnu_config $ARCH) + GCC=$(get_ndk_toolchain_prebuilt \ + "$NDK_DIR" "$ARCH" "$HOST_OS-$HOST_ARCH/bin/$CONFIG-gcc") + if [ -z "$GCC" -a "$HOST_ARCH" = "x86_64" ]; then + GCC=$(get_ndk_toolchain_prebuilt \ + "$NDK_DIR" "$ARCH" "$HOST_OS-x86/bin/$CONFIG-gcc") + fi + if [ ! -f "$GCC" -a "$ARCH" = "x86" ]; then + # Special case, the x86 toolchain used to be incorrectly + # named i686-android-linux-gcc! + GCC=$(get_ndk_toolchain_prebuilt \ + "$NDK_DIR" "$ARCH" "$HOST_OS-x86/bin/i686-android-linux-gcc") + fi + if [ -z "$GCC" ]; then + panic "Cannot find Android NDK toolchain for '$ARCH' architecture. \ +Please verify your NDK installation!" + fi + echo "${GCC%%gcc}" +} + +# $1: NDK install path +# $2: target architecture. +get_ndk_gdbserver () { + local NDK_DIR="$1" + local ARCH=$2 + local BINARY + + # The location has moved after NDK r8 + BINARY=$NDK_DIR/prebuilt/android-$ARCH/gdbserver/gdbserver + if [ ! -f "$BINARY" ]; then + BINARY=$(get_ndk_toolchain_prebuilt "$NDK_DIR" "$ARCH" gdbserver) + fi + echo "$BINARY" +} + +# Check/probe the path to the Android toolchain installation. Always +# use the NDK versions of gdb and gdbserver. They must match to avoid +# issues when both binaries do not speak the same wire protocol. +# +if [ -z "$TOOLCHAIN" ]; then + ANDROID_TOOLCHAIN=$(get_ndk_toolchain_fullprefix \ + "$ANDROID_NDK_ROOT" "$TARGET_ARCH") + ANDROID_TOOLCHAIN=$(dirname "$ANDROID_TOOLCHAIN") + log "Auto-config: --toolchain=$ANDROID_TOOLCHAIN" +else + # Be flexible, allow one to specify either the install path or the bin + # sub-directory in --toolchain: + # + if [ -d "$TOOLCHAIN/bin" ]; then + TOOLCHAIN=$TOOLCHAIN/bin + fi + ANDROID_TOOLCHAIN=$TOOLCHAIN +fi + +# Cosmetic: Remove trailing directory separator. +ANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN%/} + +# Find host GDB client binary +if [ -z "$GDB" ]; then + GDB=$(which $ANDROID_TOOLCHAIN/*-$GDBEXEPOSTFIX 2>/dev/null | head -1) + if [ -z "$GDB" ]; then + panic "Can't find Android gdb client in your path, check your \ +--toolchain or --gdb path." + fi + log "Host gdb client: $GDB" +fi + +# Find gdbserver binary, we will later push it to /data/local/tmp +# This ensures that both gdbserver and $GDB talk the same binary protocol, +# otherwise weird problems will appear. +# +if [ -z "$GDBSERVER" ]; then + GDBSERVER=$(get_ndk_gdbserver "$ANDROID_NDK_ROOT" "$TARGET_ARCH") + if [ -z "$GDBSERVER" ]; then + panic "Can't find NDK gdbserver binary. use --gdbserver to specify \ +valid one!" + fi + log "Auto-config: --gdbserver=$GDBSERVER" +fi + +# A unique ID for this script's session. This needs to be the same in all +# sub-shell commands we're going to launch, so take the PID of the launcher +# process. +TMP_ID=$$ + +# Temporary directory, will get cleaned up on exit. +TMPDIR=/tmp/$USER-adb-gdb-tmp-$TMP_ID +mkdir -p "$TMPDIR" && rm -rf "$TMPDIR"/* + +GDBSERVER_PIDFILE="$TMPDIR"/gdbserver-$TMP_ID.pid + +# If --force is specified, try to kill any gdbserver process started by the +# same user on the device. Normally, these are killed automatically by the +# script on exit, but there are a few corner cases where this would still +# be needed. +if [ "$FORCE" ]; then + GDBSERVER_PIDS=$(adb_shell ps | awk '$9 ~ /gdbserver/ { print $2; }') + for GDB_PID in $GDBSERVER_PIDS; do + log "Killing previous gdbserver (PID=$GDB_PID)" + adb_shell kill -9 $GDB_PID + done +fi + +if [ "$START" ]; then + log "Starting $PROGRAM_NAME on device." + adb_shell am start -n $PACKAGE_NAME/$ACTIVITY 2>/dev/null + adb_shell ps | grep -q $PACKAGE_NAME + fail_panic "Could not start $PROGRAM_NAME on device. Are you sure the \ +package is installed?" +fi + +# Return the timestamp of a given time, as number of seconds since epoch. +# $1: file path +# Out: file timestamp +get_file_timestamp () { + stat -c %Y "$1" 2>/dev/null +} + +# Detect the build type and symbol directory. This is done by finding +# the most recent sub-directory containing debug shared libraries under +# $CHROMIUM_SRC/$CHROMIUM_OUT_DIR/ +# +# $1: $BUILDTYPE value, can be empty +# Out: nothing, but this sets SYMBOL_DIR +# +detect_symbol_dir () { + local SUBDIRS SUBDIR LIST DIR DIR_LIBS TSTAMP + # Note: Ninja places debug libraries under out/$BUILDTYPE/lib/, while + # Make places then under out/$BUILDTYPE/lib.target. + if [ "$1" ]; then + SUBDIRS="$1/lib $1/lib.target" + else + SUBDIRS="Release/lib Debug/lib Release/lib.target Debug/lib.target" + fi + LIST=$TMPDIR/scan-subdirs-$$.txt + printf "" > "$LIST" + for SUBDIR in $SUBDIRS; do + DIR=$CHROMIUM_SRC/$CHROMIUM_OUT_DIR/$SUBDIR + if [ -d "$DIR" ]; then + # Ignore build directories that don't contain symbol versions + # of the shared libraries. + DIR_LIBS=$(ls "$DIR"/lib*.so 2>/dev/null) + if [ -z "$DIR_LIBS" ]; then + echo "No shared libs: $DIR" + continue + fi + TSTAMP=$(get_file_timestamp "$DIR") + printf "%s %s\n" "$TSTAMP" "$SUBDIR" >> "$LIST" + fi + done + SUBDIR=$(cat $LIST | sort -r | head -1 | cut -d" " -f2) + rm -f "$LIST" + + if [ -z "$SUBDIR" ]; then + if [ -z "$1" ]; then + panic "Could not find any build directory under \ +$CHROMIUM_SRC/$CHROMIUM_OUT_DIR. Please build the program first!" + else + panic "Could not find any $1 directory under \ +$CHROMIUM_SRC/$CHROMIUM_OUT_DIR. Check your build type!" + fi + fi + + SYMBOL_DIR=$CHROMIUM_SRC/$CHROMIUM_OUT_DIR/$SUBDIR + log "Auto-config: --symbol-dir=$SYMBOL_DIR" +} + +if [ -z "$SYMBOL_DIR" ]; then + detect_symbol_dir "$BUILDTYPE" +fi + +# Allow several concurrent debugging sessions +TARGET_GDBSERVER=/data/data/$PACKAGE_NAME/gdbserver-adb-gdb-$TMP_ID +TMP_TARGET_GDBSERVER=/data/local/tmp/gdbserver-adb-gdb-$TMP_ID + +# Return the build fingerprint contained in a build.prop file. +# $1: path to build.prop file +get_build_fingerprint_from () { + cat "$1" | grep -e '^ro.build.fingerprint=' | cut -d= -f2 +} + + +ORG_PULL_LIBS_DIR=$PULL_LIBS_DIR +PULL_LIBS_DIR=${PULL_LIBS_DIR:-$DEFAULT_PULL_LIBS_DIR} + +HOST_FINGERPRINT= +DEVICE_FINGERPRINT=$(adb_shell getprop ro.build.fingerprint) +log "Device build fingerprint: $DEVICE_FINGERPRINT" + +# If --pull-libs-dir is not specified, and this is a platform build, look +# if we can use the symbolic libraries under $ANDROID_PRODUCT_OUT/symbols/ +# directly, if the build fingerprint matches the device. +if [ -z "$ORG_PULL_LIBS_DIR" -a \ + "$ANDROID_PRODUCT_OUT" -a \ + -f "$ANDROID_PRODUCT_OUT/system/build.prop" ]; then + ANDROID_FINGERPRINT=$(get_build_fingerprint_from \ + "$ANDROID_PRODUCT_OUT"/system/build.prop) + log "Android build fingerprint: $ANDROID_FINGERPRINT" + if [ "$ANDROID_FINGERPRINT" = "$DEVICE_FINGERPRINT" ]; then + log "Perfect match!" + PULL_LIBS_DIR=$ANDROID_PRODUCT_OUT/symbols + HOST_FINGERPRINT=$ANDROID_FINGERPRINT + if [ "$PULL_LIBS" ]; then + log "Ignoring --pull-libs since the device and platform build \ +fingerprints match." + NO_PULL_LIBS=true + fi + fi +fi + +# If neither --pull-libs an --no-pull-libs were specified, check the build +# fingerprints of the device, and the cached system libraries on the host. +# +if [ -z "$NO_PULL_LIBS" -a -z "$PULL_LIBS" ]; then + if [ ! -f "$PULL_LIBS_DIR/build.prop" ]; then + log "Auto-config: --pull-libs (no cached libraries)" + PULL_LIBS=true + else + HOST_FINGERPRINT=$(get_build_fingerprint_from "$PULL_LIBS_DIR/build.prop") + log "Host build fingerprint: $HOST_FINGERPRINT" + if [ "$HOST_FINGERPRINT" == "$DEVICE_FINGERPRINT" ]; then + log "Auto-config: --no-pull-libs (fingerprint match)" + NO_PULL_LIBS=true + else + log "Auto-config: --pull-libs (fingerprint mismatch)" + PULL_LIBS=true + fi + fi +fi + +# Extract the system libraries from the device if necessary. +if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then + echo "Extracting system libraries into: $PULL_LIBS_DIR" +fi + +mkdir -p "$PULL_LIBS_DIR" +fail_panic "Can't create --libs-dir directory: $PULL_LIBS_DIR" + +# If requested, work for M-x gdb. The gdb indirections make it +# difficult to pass --annotate=3 to the gdb binary itself. +GDB_ARGS= +if [ "$ANNOTATE" ]; then + GDB_ARGS=$GDB_ARGS" --annotate=$ANNOTATE" +fi + +# Get the PID from the first argument or else find the PID of the +# browser process. +if [ -z "$PID" ]; then + PROCESSNAME=$PACKAGE_NAME + if [ "$SANDBOXED_INDEX" ]; then + PROCESSNAME=$PROCESSNAME:sandboxed_process$SANDBOXED_INDEX + elif [ "$SANDBOXED" ]; then + PROCESSNAME=$PROCESSNAME:sandboxed_process + PID=$(adb_shell ps | \ + awk '$9 ~ /^'$PROCESSNAME'/ { print $2; }' | head -1) + elif [ "$PRIVILEGED_INDEX" ]; then + PROCESSNAME=$PROCESSNAME:privileged_process$PRIVILEGED_INDEX + elif [ "$PRIVILEGED" ]; then + PROCESSNAME=$PROCESSNAME:privileged_process + PID=$(adb_shell ps | \ + awk '$9 ~ /^'$PROCESSNAME'/ { print $2; }' | head -1) + fi + if [ -z "$PID" ]; then + PID=$(adb_shell ps | \ + awk '$9 == "'$PROCESSNAME'" { print $2; }' | head -1) + fi + if [ -z "$PID" ]; then + if [ "$START" ]; then + panic "Can't find application process PID, did it crash?" + else + panic "Can't find application process PID, are you sure it is \ +running? Try using --start." + fi + fi + log "Found process PID: $PID" +elif [ "$SANDBOXED" ]; then + echo "WARNING: --sandboxed option ignored due to use of --pid." +elif [ "$PRIVILEGED" ]; then + echo "WARNING: --privileged option ignored due to use of --pid." +fi + +# Determine if 'adb shell' runs as root or not. +# If so, we can launch gdbserver directly, otherwise, we have to +# use run-as $PACKAGE_NAME ..., which requires the package to be debuggable. +# +if [ "$SU_PREFIX" ]; then + # Need to check that this works properly. + SU_PREFIX_TEST_LOG=$TMPDIR/su-prefix.log + adb_shell $SU_PREFIX \"echo "foo"\" > $SU_PREFIX_TEST_LOG 2>&1 + if [ $? != 0 -o "$(cat $SU_PREFIX_TEST_LOG)" != "foo" ]; then + echo "ERROR: Cannot use '$SU_PREFIX' as a valid su prefix:" + echo "$ adb shell $SU_PREFIX \"echo foo\"" + cat $SU_PREFIX_TEST_LOG + exit 1 + fi + COMMAND_PREFIX="$SU_PREFIX \"" + COMMAND_SUFFIX="\"" +else + SHELL_UID=$(adb shell cat /proc/self/status | \ + awk '$1 == "Uid:" { print $2; }') + log "Shell UID: $SHELL_UID" + if [ "$SHELL_UID" != 0 -o -n "$NO_ROOT" ]; then + COMMAND_PREFIX="run-as $PACKAGE_NAME" + COMMAND_SUFFIX= + else + COMMAND_PREFIX= + COMMAND_SUFFIX= + fi +fi +log "Command prefix: '$COMMAND_PREFIX'" +log "Command suffix: '$COMMAND_SUFFIX'" + +# Pull device's system libraries that are mapped by our process. +# Pulling all system libraries is too long, so determine which ones +# we need by looking at /proc/$PID/maps instead +if [ "$PULL_LIBS" -a -z "$NO_PULL_LIBS" ]; then + echo "Extracting system libraries into: $PULL_LIBS_DIR" + rm -f $PULL_LIBS_DIR/build.prop + MAPPINGS=$(adb_shell $COMMAND_PREFIX cat /proc/$PID/maps $COMMAND_SUFFIX) + if [ $? != 0 ]; then + echo "ERROR: Could not list process's memory mappings." + if [ "$SU_PREFIX" ]; then + panic "Are you sure your --su-prefix is correct?" + else + panic "Use --su-prefix if the application is not debuggable." + fi + fi + SYSTEM_LIBS=$(echo "$MAPPINGS" | \ + awk '$6 ~ /\/system\/.*\.so$/ { print $6; }' | sort -u) + for SYSLIB in /system/bin/linker $SYSTEM_LIBS; do + echo "Pulling from device: $SYSLIB" + DST_FILE=$PULL_LIBS_DIR$SYSLIB + DST_DIR=$(dirname "$DST_FILE") + mkdir -p "$DST_DIR" && adb pull $SYSLIB "$DST_FILE" 2>/dev/null + fail_panic "Could not pull $SYSLIB from device !?" + done + echo "Pulling device build.prop" + adb pull /system/build.prop $PULL_LIBS_DIR/build.prop + fail_panic "Could not pull device build.prop !?" +fi + +# Find all the sub-directories of $PULL_LIBS_DIR, up to depth 4 +# so we can add them to solib-search-path later. +SOLIB_DIRS=$(find $PULL_LIBS_DIR -mindepth 1 -maxdepth 4 -type d | \ + grep -v "^$" | tr '\n' ':') + +# This is a re-implementation of gdbclient, where we use compatible +# versions of gdbserver and $GDBNAME to ensure that everything works +# properly. +# + +# Push gdbserver to the device +log "Pushing gdbserver $GDBSERVER to $TARGET_GDBSERVER" +adb push $GDBSERVER $TMP_TARGET_GDBSERVER &>/dev/null +adb shell $COMMAND_PREFIX cp $TMP_TARGET_GDBSERVER $TARGET_GDBSERVER +adb shell rm $TMP_TARGET_GDBSERVER +fail_panic "Could not copy gdbserver to the device!" + +if [ -z "$PORT" ]; then + PORT=5039 +fi +HOST_PORT=$PORT +TARGET_PORT=$PORT + +# Select correct app_process for architecture. +case $TARGET_ARCH in + arm|x86|mips) GDBEXEC=app_process;; + arm64|x86_64) GDBEXEC=app_process64;; + *) fail_panic "Unknown app_process for architecture!";; +esac + +# Detect AddressSanitizer setup on the device. In that case app_process is a +# script, and the real executable is app_process.real. +GDBEXEC_ASAN=app_process.real +adb_shell ls /system/bin/$GDBEXEC_ASAN +if [ $? == 0 ]; then + GDBEXEC=$GDBEXEC_ASAN +fi + +# Pull the app_process binary from the device. +log "Pulling $GDBEXEC from device" +adb pull /system/bin/$GDBEXEC "$TMPDIR"/$GDBEXEC &>/dev/null +fail_panic "Could not retrieve $GDBEXEC from the device!" + +# Setup network redirection +log "Setting network redirection (host:$HOST_PORT -> device:$TARGET_PORT)" +adb forward tcp:$HOST_PORT tcp:$TARGET_PORT +fail_panic "Could not setup network redirection from \ +host:localhost:$HOST_PORT to device:localhost:$TARGET_PORT!" + +# Start gdbserver in the background +# Note that using run-as requires the package to be debuggable. +# +# If not, this will fail horribly. The alternative is to run the +# program as root, which requires of course root privileges. +# Maybe we should add a --root option to enable this? +# +log "Starting gdbserver in the background:" +GDBSERVER_LOG=$TMPDIR/gdbserver-$TMP_ID.log +log "adb shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \ +--attach $PID $COMMAND_SUFFIX" +("$ADB" shell $COMMAND_PREFIX $TARGET_GDBSERVER :$TARGET_PORT \ + --attach $PID $COMMAND_SUFFIX > $GDBSERVER_LOG 2>&1) & +GDBSERVER_PID=$! +echo "$GDBSERVER_PID" > $GDBSERVER_PIDFILE +log "background job pid: $GDBSERVER_PID" + +# Check that it is still running after a few seconds. If not, this means we +# could not properly attach to it +sleep 2 +log "Job control: $(jobs -l)" +STATE=$(jobs -l | awk '$2 == "'$GDBSERVER_PID'" { print $3; }') +if [ "$STATE" != "Running" ]; then + echo "ERROR: GDBServer could not attach to PID $PID!" + if [ $(adb_shell su -c getenforce) != "Permissive" ]; then + echo "Device mode is Enforcing. Changing Device mode to Permissive " + $(adb_shell su -c setenforce 0) + if [ $(adb_shell su -c getenforce) != "Permissive" ]; then + echo "ERROR: Failed to Change Device mode to Permissive" + echo "Failure log (use --verbose for more information):" + cat $GDBSERVER_LOG + exit 1 + fi + else + echo "Failure log (use --verbose for more information):" + cat $GDBSERVER_LOG + exit 1 + fi +fi + +# Generate a file containing useful GDB initialization commands +readonly COMMANDS=$TMPDIR/gdb.init +log "Generating GDB initialization commands file: $COMMANDS" +echo -n "" > $COMMANDS +echo "set print pretty 1" >> $COMMANDS +echo "python" >> $COMMANDS +echo "import sys" >> $COMMANDS +echo "sys.path.insert(0, '$CHROMIUM_SRC/tools/gdb/')" >> $COMMANDS +echo "try:" >> $COMMANDS +echo " import gdb_chrome" >> $COMMANDS +echo "finally:" >> $COMMANDS +echo " sys.path.pop(0)" >> $COMMANDS +echo "end" >> $COMMANDS +echo "file $TMPDIR/$GDBEXEC" >> $COMMANDS +echo "directory $CHROMIUM_SRC" >> $COMMANDS +echo "set solib-absolute-prefix $PULL_LIBS_DIR" >> $COMMANDS +echo "set solib-search-path $SOLIB_DIRS:$PULL_LIBS_DIR:$SYMBOL_DIR" \ + >> $COMMANDS +echo "echo Attaching and reading symbols, this may take a while.." \ + >> $COMMANDS +echo "target remote :$HOST_PORT" >> $COMMANDS + +if [ "$GDBINIT" ]; then + cat "$GDBINIT" >> $COMMANDS +fi + +if [ "$VERBOSE" -gt 0 ]; then + echo "### START $COMMANDS" + cat $COMMANDS + echo "### END $COMMANDS" +fi + +log "Launching gdb client: $GDB $GDB_ARGS -x $COMMANDS" +$GDB $GDB_ARGS -x $COMMANDS && +rm -f "$GDBSERVER_PIDFILE" diff --git a/engine/src/flutter/build/android/adb_gdb_android_webview_shell b/engine/src/flutter/build/android/adb_gdb_android_webview_shell new file mode 100755 index 0000000000..f685fda77c --- /dev/null +++ b/engine/src/flutter/build/android/adb_gdb_android_webview_shell @@ -0,0 +1,16 @@ +#!/bin/bash +# +# 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. +# +# Attach to or start a ContentShell process and debug it. +# See --help for details. +# +PROGDIR=$(dirname "$0") +export ADB_GDB_PROGNAME=$(basename "$0") +export ADB_GDB_ACTIVITY=.AwShellActivity +"$PROGDIR"/adb_gdb \ + --program-name=AwShellApplication \ + --package-name=org.chromium.android_webview.shell \ + "$@" diff --git a/engine/src/flutter/build/android/adb_gdb_chrome_public b/engine/src/flutter/build/android/adb_gdb_chrome_public new file mode 100755 index 0000000000..4366c838e7 --- /dev/null +++ b/engine/src/flutter/build/android/adb_gdb_chrome_public @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright 2015 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. +# +# Attach to or start a ChromePublic process and debug it. +# See --help for details. +# +PROGDIR=$(dirname "$0") +export ADB_GDB_PROGNAME=$(basename "$0") +export ADB_GDB_ACTIVITY=com.google.android.apps.chrome.Main +"$PROGDIR"/adb_gdb \ + --program-name=ChromePublic \ + --package-name=org.chromium.chrome \ + "$@" diff --git a/engine/src/flutter/build/android/adb_gdb_chrome_shell b/engine/src/flutter/build/android/adb_gdb_chrome_shell new file mode 100755 index 0000000000..e5c8a306be --- /dev/null +++ b/engine/src/flutter/build/android/adb_gdb_chrome_shell @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright 2014 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. +# +# Attach to or start a ChromeShell process and debug it. +# See --help for details. +# +PROGDIR=$(dirname "$0") +export ADB_GDB_PROGNAME=$(basename "$0") +export ADB_GDB_ACTIVITY=.ChromeShellActivity +"$PROGDIR"/adb_gdb \ + --program-name=ChromeShell \ + --package-name=org.chromium.chrome.shell \ + "$@" diff --git a/engine/src/flutter/build/android/adb_gdb_content_shell b/engine/src/flutter/build/android/adb_gdb_content_shell new file mode 100755 index 0000000000..18e1a61d89 --- /dev/null +++ b/engine/src/flutter/build/android/adb_gdb_content_shell @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright (c) 2012 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. +# +# Attach to or start a ContentShell process and debug it. +# See --help for details. +# +PROGDIR=$(dirname "$0") +export ADB_GDB_PROGNAME=$(basename "$0") +export ADB_GDB_ACTIVITY=.ContentShellActivity +"$PROGDIR"/adb_gdb \ + --program-name=ContentShell \ + --package-name=org.chromium.content_shell_apk \ + "$@" diff --git a/engine/src/flutter/build/android/adb_gdb_cronet_sample b/engine/src/flutter/build/android/adb_gdb_cronet_sample new file mode 100755 index 0000000000..8d0c864d13 --- /dev/null +++ b/engine/src/flutter/build/android/adb_gdb_cronet_sample @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright 2014 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. +# +# Attach to or start a ContentShell process and debug it. +# See --help for details. +# +PROGDIR=$(dirname "$0") +export ADB_GDB_PROGNAME=$(basename "$0") +export ADB_GDB_ACTIVITY=.CronetSampleActivity +"$PROGDIR"/adb_gdb \ + --program-name=CronetSample \ + --package-name=org.chromium.cronet_sample_apk \ + "$@" diff --git a/engine/src/flutter/build/android/adb_gdb_mojo_shell b/engine/src/flutter/build/android/adb_gdb_mojo_shell new file mode 100755 index 0000000000..ba91149cce --- /dev/null +++ b/engine/src/flutter/build/android/adb_gdb_mojo_shell @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright 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. +# +# Attach to or start a ContentShell process and debug it. +# See --help for details. +# +PROGDIR=$(dirname "$0") +export ADB_GDB_PROGNAME=$(basename "$0") +export ADB_GDB_ACTIVITY=.MojoShellActivity +"$PROGDIR"/adb_gdb \ + --program-name=MojoShell \ + --package-name=org.chromium.mojo_shell_apk \ + "$@" diff --git a/engine/src/flutter/build/android/adb_install_apk.py b/engine/src/flutter/build/android/adb_install_apk.py new file mode 100755 index 0000000000..00bd38e182 --- /dev/null +++ b/engine/src/flutter/build/android/adb_install_apk.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 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. + +"""Utility script to install APKs from the command line quickly.""" + +import optparse +import os +import sys + +from pylib import constants +from pylib.device import device_errors +from pylib.device import device_utils + + +def AddInstallAPKOption(option_parser): + """Adds apk option used to install the APK to the OptionParser.""" + option_parser.add_option('--apk', + help=('DEPRECATED The name of the apk containing the' + ' application (with the .apk extension).')) + option_parser.add_option('--apk_package', + help=('DEPRECATED The package name used by the apk ' + 'containing the application.')) + option_parser.add_option('--keep_data', + action='store_true', + default=False, + help=('Keep the package data when installing ' + 'the application.')) + option_parser.add_option('--debug', action='store_const', const='Debug', + dest='build_type', + default=os.environ.get('BUILDTYPE', 'Debug'), + help='If set, run test suites under out/Debug. ' + 'Default is env var BUILDTYPE or Debug') + option_parser.add_option('--release', action='store_const', const='Release', + dest='build_type', + help='If set, run test suites under out/Release. ' + 'Default is env var BUILDTYPE or Debug.') + option_parser.add_option('-d', '--device', dest='device', + help='Target device for apk to install on.') + + +def ValidateInstallAPKOption(option_parser, options, args): + """Validates the apk option and potentially qualifies the path.""" + if not options.apk: + if len(args) > 1: + options.apk = args[1] + else: + option_parser.error('apk target not specified.') + + if not options.apk.endswith('.apk'): + options.apk += '.apk' + + if not os.path.exists(options.apk): + options.apk = os.path.join(constants.GetOutDirectory(), 'apks', + options.apk) + + +def main(argv): + parser = optparse.OptionParser() + parser.set_usage("usage: %prog [options] target") + AddInstallAPKOption(parser) + options, args = parser.parse_args(argv) + + if len(args) > 1 and options.apk: + parser.error("Appending the apk as argument can't be used with --apk.") + elif len(args) > 2: + parser.error("Too many arguments.") + + constants.SetBuildType(options.build_type) + ValidateInstallAPKOption(parser, options, args) + + devices = device_utils.DeviceUtils.HealthyDevices() + + if options.device: + devices = [d for d in devices if d == options.device] + if not devices: + raise device_errors.DeviceUnreachableError(options.device) + elif not devices: + raise device_errors.NoDevicesError() + + device_utils.DeviceUtils.parallel(devices).Install( + options.apk, reinstall=options.keep_data) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) + diff --git a/engine/src/flutter/build/android/adb_kill_android_webview_shell b/engine/src/flutter/build/android/adb_kill_android_webview_shell new file mode 100755 index 0000000000..5f287f0826 --- /dev/null +++ b/engine/src/flutter/build/android/adb_kill_android_webview_shell @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright 2015 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. +# +# Kill a running android webview shell. +# +# Assumes you have sourced the build/android/envsetup.sh script. + +SHELL_PID_LINES=$(adb shell ps | grep ' org.chromium.android_webview.shell') +VAL=$(echo "$SHELL_PID_LINES" | wc -l) +if [ $VAL -lt 1 ] ; then + echo "Not running android webview shell." +else + SHELL_PID=$(echo $SHELL_PID_LINES | awk '{print $2}') + if [ "$SHELL_PID" != "" ] ; then + set -x + adb shell kill $SHELL_PID + set - + else + echo "Android webview shell does not appear to be running." + fi +fi diff --git a/engine/src/flutter/build/android/adb_kill_chrome_public b/engine/src/flutter/build/android/adb_kill_chrome_public new file mode 100755 index 0000000000..5b539a043d --- /dev/null +++ b/engine/src/flutter/build/android/adb_kill_chrome_public @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright 2015 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. +# +# Kill a running instance of ChromePublic. +# +# Assumes you have sourced the build/android/envsetup.sh script. + +SHELL_PID_LINES=$(adb shell ps | grep -w 'org.chromium.chrome') +VAL=$(echo "$SHELL_PID_LINES" | wc -l) +if [ $VAL -lt 1 ] ; then + echo "Not running ChromePublic." +else + SHELL_PID=$(echo $SHELL_PID_LINES | awk '{print $2}') + if [ "$SHELL_PID" != "" ] ; then + set -x + adb shell kill $SHELL_PID + set - + else + echo "ChromePublic does not appear to be running." + fi +fi diff --git a/engine/src/flutter/build/android/adb_kill_chrome_shell b/engine/src/flutter/build/android/adb_kill_chrome_shell new file mode 100755 index 0000000000..2b63c9af3d --- /dev/null +++ b/engine/src/flutter/build/android/adb_kill_chrome_shell @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright (c) 2012 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. +# +# Kill a running chrome shell. +# +# Assumes you have sourced the build/android/envsetup.sh script. + +SHELL_PID_LINES=$(adb shell ps | grep ' org.chromium.chrome.shell') +VAL=$(echo "$SHELL_PID_LINES" | wc -l) +if [ $VAL -lt 1 ] ; then + echo "Not running Chrome shell." +else + SHELL_PID=$(echo $SHELL_PID_LINES | awk '{print $2}') + if [ "$SHELL_PID" != "" ] ; then + set -x + adb shell kill $SHELL_PID + set - + else + echo "Chrome shell does not appear to be running." + fi +fi diff --git a/engine/src/flutter/build/android/adb_kill_content_shell b/engine/src/flutter/build/android/adb_kill_content_shell new file mode 100755 index 0000000000..e379dd4714 --- /dev/null +++ b/engine/src/flutter/build/android/adb_kill_content_shell @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright (c) 2012 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. +# +# Kill a running content shell. +# +# Assumes you have sourced the build/android/envsetup.sh script. + +SHELL_PID_LINES=$(adb shell ps | grep ' org.chromium.content_shell_apk') +VAL=$(echo "$SHELL_PID_LINES" | wc -l) +if [ $VAL -lt 1 ] ; then + echo "Not running Content shell." +else + SHELL_PID=$(echo $SHELL_PID_LINES | awk '{print $2}') + if [ "$SHELL_PID" != "" ] ; then + set -x + adb shell kill $SHELL_PID + set - + else + echo "Content shell does not appear to be running." + fi +fi diff --git a/engine/src/flutter/build/android/adb_logcat_monitor.py b/engine/src/flutter/build/android/adb_logcat_monitor.py new file mode 100755 index 0000000000..d3cc67dbcc --- /dev/null +++ b/engine/src/flutter/build/android/adb_logcat_monitor.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 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. + +"""Saves logcats from all connected devices. + +Usage: adb_logcat_monitor.py [] + +This script will repeatedly poll adb for new devices and save logcats +inside the directory, which it attempts to create. The +script will run until killed by an external signal. To test, run the +script in a shell and -C it after a while. It should be +resilient across phone disconnects and reconnects and start the logcat +early enough to not miss anything. +""" + +import logging +import os +import re +import shutil +import signal +import subprocess +import sys +import time + +# Map from device_id -> (process, logcat_num) +devices = {} + + +class TimeoutException(Exception): + """Exception used to signal a timeout.""" + pass + + +class SigtermError(Exception): + """Exception used to catch a sigterm.""" + pass + + +def StartLogcatIfNecessary(device_id, adb_cmd, base_dir): + """Spawns a adb logcat process if one is not currently running.""" + process, logcat_num = devices[device_id] + if process: + if process.poll() is None: + # Logcat process is still happily running + return + else: + logging.info('Logcat for device %s has died', device_id) + error_filter = re.compile('- waiting for device -') + for line in process.stderr: + if not error_filter.match(line): + logging.error(device_id + ': ' + line) + + logging.info('Starting logcat %d for device %s', logcat_num, + device_id) + logcat_filename = 'logcat_%s_%03d' % (device_id, logcat_num) + logcat_file = open(os.path.join(base_dir, logcat_filename), 'w') + process = subprocess.Popen([adb_cmd, '-s', device_id, + 'logcat', '-v', 'threadtime'], + stdout=logcat_file, + stderr=subprocess.PIPE) + devices[device_id] = (process, logcat_num + 1) + + +def GetAttachedDevices(adb_cmd): + """Gets the device list from adb. + + We use an alarm in this function to avoid deadlocking from an external + dependency. + + Args: + adb_cmd: binary to run adb + + Returns: + list of devices or an empty list on timeout + """ + signal.alarm(2) + try: + out, err = subprocess.Popen([adb_cmd, 'devices'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + if err: + logging.warning('adb device error %s', err.strip()) + return re.findall('^(\\S+)\tdevice$', out, re.MULTILINE) + except TimeoutException: + logging.warning('"adb devices" command timed out') + return [] + except (IOError, OSError): + logging.exception('Exception from "adb devices"') + return [] + finally: + signal.alarm(0) + + +def main(base_dir, adb_cmd='adb'): + """Monitor adb forever. Expects a SIGINT (Ctrl-C) to kill.""" + # We create the directory to ensure 'run once' semantics + if os.path.exists(base_dir): + print 'adb_logcat_monitor: %s already exists? Cleaning' % base_dir + shutil.rmtree(base_dir, ignore_errors=True) + + os.makedirs(base_dir) + logging.basicConfig(filename=os.path.join(base_dir, 'eventlog'), + level=logging.INFO, + format='%(asctime)-2s %(levelname)-8s %(message)s') + + # Set up the alarm for calling 'adb devices'. This is to ensure + # our script doesn't get stuck waiting for a process response + def TimeoutHandler(_signum, _unused_frame): + raise TimeoutException() + signal.signal(signal.SIGALRM, TimeoutHandler) + + # Handle SIGTERMs to ensure clean shutdown + def SigtermHandler(_signum, _unused_frame): + raise SigtermError() + signal.signal(signal.SIGTERM, SigtermHandler) + + logging.info('Started with pid %d', os.getpid()) + pid_file_path = os.path.join(base_dir, 'LOGCAT_MONITOR_PID') + + try: + with open(pid_file_path, 'w') as f: + f.write(str(os.getpid())) + while True: + for device_id in GetAttachedDevices(adb_cmd): + if not device_id in devices: + subprocess.call([adb_cmd, '-s', device_id, 'logcat', '-c']) + devices[device_id] = (None, 0) + + for device in devices: + # This will spawn logcat watchers for any device ever detected + StartLogcatIfNecessary(device, adb_cmd, base_dir) + + time.sleep(5) + except SigtermError: + logging.info('Received SIGTERM, shutting down') + except: # pylint: disable=bare-except + logging.exception('Unexpected exception in main.') + finally: + for process, _ in devices.itervalues(): + if process: + try: + process.terminate() + except OSError: + pass + os.remove(pid_file_path) + + +if __name__ == '__main__': + if 2 <= len(sys.argv) <= 3: + print 'adb_logcat_monitor: Initializing' + sys.exit(main(*sys.argv[1:3])) + + print 'Usage: %s []' % sys.argv[0] diff --git a/engine/src/flutter/build/android/adb_logcat_printer.py b/engine/src/flutter/build/android/adb_logcat_printer.py new file mode 100755 index 0000000000..55176ab920 --- /dev/null +++ b/engine/src/flutter/build/android/adb_logcat_printer.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 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. + +"""Shutdown adb_logcat_monitor and print accumulated logs. + +To test, call './adb_logcat_printer.py ' where + contains 'adb logcat -v threadtime' files named as +logcat__ + +The script will print the files to out, and will combine multiple +logcats from a single device if there is overlap. + +Additionally, if a /LOGCAT_MONITOR_PID exists, the script +will attempt to terminate the contained PID by sending a SIGINT and +monitoring for the deletion of the aforementioned file. +""" +# pylint: disable=W0702 + +import cStringIO +import logging +import optparse +import os +import re +import signal +import sys +import time + + +# Set this to debug for more verbose output +LOG_LEVEL = logging.INFO + + +def CombineLogFiles(list_of_lists, logger): + """Splices together multiple logcats from the same device. + + Args: + list_of_lists: list of pairs (filename, list of timestamped lines) + logger: handler to log events + + Returns: + list of lines with duplicates removed + """ + cur_device_log = [''] + for cur_file, cur_file_lines in list_of_lists: + # Ignore files with just the logcat header + if len(cur_file_lines) < 2: + continue + common_index = 0 + # Skip this step if list just has empty string + if len(cur_device_log) > 1: + try: + line = cur_device_log[-1] + # Used to make sure we only splice on a timestamped line + if re.match(r'^\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3} ', line): + common_index = cur_file_lines.index(line) + else: + logger.warning('splice error - no timestamp in "%s"?', line.strip()) + except ValueError: + # The last line was valid but wasn't found in the next file + cur_device_log += ['***** POSSIBLE INCOMPLETE LOGCAT *****'] + logger.info('Unable to splice %s. Incomplete logcat?', cur_file) + + cur_device_log += ['*'*30 + ' %s' % cur_file] + cur_device_log.extend(cur_file_lines[common_index:]) + + return cur_device_log + + +def FindLogFiles(base_dir): + """Search a directory for logcat files. + + Args: + base_dir: directory to search + + Returns: + Mapping of device_id to a sorted list of file paths for a given device + """ + logcat_filter = re.compile(r'^logcat_(\S+)_(\d+)$') + # list of tuples (, , ) + filtered_list = [] + for cur_file in os.listdir(base_dir): + matcher = logcat_filter.match(cur_file) + if matcher: + filtered_list += [(matcher.group(1), int(matcher.group(2)), + os.path.join(base_dir, cur_file))] + filtered_list.sort() + file_map = {} + for device_id, _, cur_file in filtered_list: + if device_id not in file_map: + file_map[device_id] = [] + + file_map[device_id] += [cur_file] + return file_map + + +def GetDeviceLogs(log_filenames, logger): + """Read log files, combine and format. + + Args: + log_filenames: mapping of device_id to sorted list of file paths + logger: logger handle for logging events + + Returns: + list of formatted device logs, one for each device. + """ + device_logs = [] + + for device, device_files in log_filenames.iteritems(): + logger.debug('%s: %s', device, str(device_files)) + device_file_lines = [] + for cur_file in device_files: + with open(cur_file) as f: + device_file_lines += [(cur_file, f.read().splitlines())] + combined_lines = CombineLogFiles(device_file_lines, logger) + # Prepend each line with a short unique ID so it's easy to see + # when the device changes. We don't use the start of the device + # ID because it can be the same among devices. Example lines: + # AB324: foo + # AB324: blah + device_logs += [('\n' + device[-5:] + ': ').join(combined_lines)] + return device_logs + + +def ShutdownLogcatMonitor(base_dir, logger): + """Attempts to shutdown adb_logcat_monitor and blocks while waiting.""" + try: + monitor_pid_path = os.path.join(base_dir, 'LOGCAT_MONITOR_PID') + with open(monitor_pid_path) as f: + monitor_pid = int(f.readline()) + + logger.info('Sending SIGTERM to %d', monitor_pid) + os.kill(monitor_pid, signal.SIGTERM) + i = 0 + while True: + time.sleep(.2) + if not os.path.exists(monitor_pid_path): + return + if not os.path.exists('/proc/%d' % monitor_pid): + logger.warning('Monitor (pid %d) terminated uncleanly?', monitor_pid) + return + logger.info('Waiting for logcat process to terminate.') + i += 1 + if i >= 10: + logger.warning('Monitor pid did not terminate. Continuing anyway.') + return + + except (ValueError, IOError, OSError): + logger.exception('Error signaling logcat monitor - continuing') + + +def main(argv): + parser = optparse.OptionParser(usage='Usage: %prog [options] ') + parser.add_option('--output-path', + help='Output file path (if unspecified, prints to stdout)') + options, args = parser.parse_args(argv) + if len(args) != 1: + parser.error('Wrong number of unparsed args') + base_dir = args[0] + if options.output_path: + output_file = open(options.output_path, 'w') + else: + output_file = sys.stdout + + log_stringio = cStringIO.StringIO() + logger = logging.getLogger('LogcatPrinter') + logger.setLevel(LOG_LEVEL) + sh = logging.StreamHandler(log_stringio) + sh.setFormatter(logging.Formatter('%(asctime)-2s %(levelname)-8s' + ' %(message)s')) + logger.addHandler(sh) + + try: + # Wait at least 5 seconds after base_dir is created before printing. + # + # The idea is that 'adb logcat > file' output consists of 2 phases: + # 1 Dump all the saved logs to the file + # 2 Stream log messages as they are generated + # + # We want to give enough time for phase 1 to complete. There's no + # good method to tell how long to wait, but it usually only takes a + # second. On most bots, this code path won't occur at all, since + # adb_logcat_monitor.py command will have spawned more than 5 seconds + # prior to called this shell script. + try: + sleep_time = 5 - (time.time() - os.path.getctime(base_dir)) + except OSError: + sleep_time = 5 + if sleep_time > 0: + logger.warning('Monitor just started? Sleeping %.1fs', sleep_time) + time.sleep(sleep_time) + + assert os.path.exists(base_dir), '%s does not exist' % base_dir + ShutdownLogcatMonitor(base_dir, logger) + separator = '\n' + '*' * 80 + '\n\n' + for log in GetDeviceLogs(FindLogFiles(base_dir), logger): + output_file.write(log) + output_file.write(separator) + with open(os.path.join(base_dir, 'eventlog')) as f: + output_file.write('\nLogcat Monitor Event Log\n') + output_file.write(f.read()) + except: + logger.exception('Unexpected exception') + + logger.info('Done.') + sh.flush() + output_file.write('\nLogcat Printer Event Log\n') + output_file.write(log_stringio.getvalue()) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/adb_profile_chrome b/engine/src/flutter/build/android/adb_profile_chrome new file mode 100755 index 0000000000..21f6faf7b9 --- /dev/null +++ b/engine/src/flutter/build/android/adb_profile_chrome @@ -0,0 +1,8 @@ +#!/bin/bash +# +# Copyright 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. +# +# Start / stop profiling in chrome. +exec $(dirname $0)/../../tools/profile_chrome.py $@ diff --git a/engine/src/flutter/build/android/adb_reverse_forwarder.py b/engine/src/flutter/build/android/adb_reverse_forwarder.py new file mode 100755 index 0000000000..3ce53595da --- /dev/null +++ b/engine/src/flutter/build/android/adb_reverse_forwarder.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# +# 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. + +"""Command line tool for forwarding ports from a device to the host. + +Allows an Android device to connect to services running on the host machine, +i.e., "adb forward" in reverse. Requires |host_forwarder| and |device_forwarder| +to be built. +""" + +import logging +import optparse +import sys +import time + +from pylib import constants +from pylib import forwarder +from pylib.device import adb_wrapper +from pylib.device import device_errors +from pylib.device import device_utils +from pylib.utils import run_tests_helper + + +def main(argv): + parser = optparse.OptionParser(usage='Usage: %prog [options] device_port ' + 'host_port [device_port_2 host_port_2] ...', + description=__doc__) + parser.add_option('-v', + '--verbose', + dest='verbose_count', + default=0, + action='count', + help='Verbose level (multiple times for more)') + parser.add_option('--device', + help='Serial number of device we should use.') + parser.add_option('--debug', action='store_const', const='Debug', + dest='build_type', default='Release', + help='Use Debug build of host tools instead of Release.') + + options, args = parser.parse_args(argv) + run_tests_helper.SetLogLevel(options.verbose_count) + + if len(args) < 2 or not len(args) % 2: + parser.error('Need even number of port pairs') + sys.exit(1) + + try: + port_pairs = map(int, args[1:]) + port_pairs = zip(port_pairs[::2], port_pairs[1::2]) + except ValueError: + parser.error('Bad port number') + sys.exit(1) + + devices = device_utils.DeviceUtils.HealthyDevices() + + if options.device: + device = next((d for d in devices if d == options.device), None) + if not device: + raise device_errors.DeviceUnreachableError(options.device) + elif devices: + device = devices[0] + logging.info('No device specified. Defaulting to %s', devices[0]) + else: + raise device_errors.NoDevicesError() + + constants.SetBuildType(options.build_type) + try: + forwarder.Forwarder.Map(port_pairs, device) + while True: + time.sleep(60) + except KeyboardInterrupt: + sys.exit(0) + finally: + forwarder.Forwarder.UnmapAllDevicePorts(device) + +if __name__ == '__main__': + main(sys.argv) diff --git a/engine/src/flutter/build/android/adb_run_android_webview_shell b/engine/src/flutter/build/android/adb_run_android_webview_shell new file mode 100755 index 0000000000..1014a731f4 --- /dev/null +++ b/engine/src/flutter/build/android/adb_run_android_webview_shell @@ -0,0 +1,12 @@ +#!/bin/bash +# +# 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. + +optional_url=$1 + +adb shell am start \ + -a android.intent.action.VIEW \ + -n org.chromium.android_webview.shell/.AwShellActivity \ + ${optional_url:+-d "$optional_url"} diff --git a/engine/src/flutter/build/android/adb_run_chrome_public b/engine/src/flutter/build/android/adb_run_chrome_public new file mode 100755 index 0000000000..bf15071144 --- /dev/null +++ b/engine/src/flutter/build/android/adb_run_chrome_public @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright 2015 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. + +optional_url=$1 + +adb shell am start \ + -a android.intent.action.VIEW \ + -n org.chromium.chrome/com.google.android.apps.chrome.Main \ + ${optional_url:+-d "$optional_url"} diff --git a/engine/src/flutter/build/android/adb_run_chrome_shell b/engine/src/flutter/build/android/adb_run_chrome_shell new file mode 100755 index 0000000000..79c4c32b5c --- /dev/null +++ b/engine/src/flutter/build/android/adb_run_chrome_shell @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright 2014 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. + +optional_url=$1 + +adb shell am start \ + -a android.intent.action.VIEW \ + -n org.chromium.chrome.shell/.ChromeShellActivity \ + ${optional_url:+-d "$optional_url"} diff --git a/engine/src/flutter/build/android/adb_run_content_shell b/engine/src/flutter/build/android/adb_run_content_shell new file mode 100755 index 0000000000..3f01f3bf02 --- /dev/null +++ b/engine/src/flutter/build/android/adb_run_content_shell @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright (c) 2012 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. + +optional_url=$1 + +adb shell am start \ + -a android.intent.action.VIEW \ + -n org.chromium.content_shell_apk/.ContentShellActivity \ + ${optional_url:+-d "$optional_url"} diff --git a/engine/src/flutter/build/android/adb_run_mojo_shell b/engine/src/flutter/build/android/adb_run_mojo_shell new file mode 100755 index 0000000000..b585e4a71f --- /dev/null +++ b/engine/src/flutter/build/android/adb_run_mojo_shell @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright 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. + +optional_url=$1 +parameters=$2 + +adb logcat -c +adb shell am start -S \ + -a android.intent.action.VIEW \ + -n org.chromium.mojo_shell_apk/.MojoShellActivity \ + ${parameters:+--esa parameters "$parameters"} \ + ${optional_url:+-d "$optional_url"} +adb logcat -s MojoShellApplication MojoShellActivity chromium diff --git a/engine/src/flutter/build/android/android_no_jni_exports.lst b/engine/src/flutter/build/android/android_no_jni_exports.lst new file mode 100644 index 0000000000..ffc6cf7028 --- /dev/null +++ b/engine/src/flutter/build/android/android_no_jni_exports.lst @@ -0,0 +1,17 @@ +# Copyright 2014 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. + +# This script makes all JNI exported symbols local, to prevent the JVM from +# being able to find them, enforcing use of manual JNI function registration. +# This is used for all Android binaries by default, unless they explicitly state +# that they want JNI exported symbols to remain visible, as we need to ensure +# the manual registration path is correct to maintain compatibility with the +# crazy linker. +# Check ld version script manual: +# https://sourceware.org/binutils/docs-2.24/ld/VERSION.html#VERSION + +{ + local: + Java_*; +}; diff --git a/engine/src/flutter/build/android/ant/apk-package.xml b/engine/src/flutter/build/android/ant/apk-package.xml new file mode 100644 index 0000000000..e8b76f7e45 --- /dev/null +++ b/engine/src/flutter/build/android/ant/apk-package.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/src/flutter/build/android/ant/chromium-debug.keystore b/engine/src/flutter/build/android/ant/chromium-debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..67eb0aa34c5af88603ee1e2dedf4bfee3e33be2c GIT binary patch literal 2223 zcmezO_TO6u1_mYu1_nkj7D&!0%FoR#%}q&7DoxK$tz=+ewA>i;bp`_iD~mxBD}wAci(dwyW|m5>^vNpE&E@P096b?8*_67@X(=9YQsLu*Y-?S)Y` z!u#5-Z<@PjmN0JOwVm}-i}ib!}4z@D2pj= zl;iW)@(g||*{d)G}k(#@}a8iims?hYhHP@70E#%+Z@TF8Lg0IHov0Q(rL3jS` zo5lCzQ+1ePGkV=0oh-QaV+*72o7rx!SC>BBdNi*U);J$CG;Fe)_Njt*+Po)I4xGJw-sD+)o3GfHCjy(#PPy?;V(H6U?k8vP zPUx9)@*LZHu}8X3eKViNESqNeAjr({NY0MfB3{G$moCQz)vaXGVtHufWqmBhW@k%ct4wpG;pI@|xFi=a2go?sGy`e)aDrR-KuhSbgtesWkJNb(3Z; z+Fy6Y9VG`5yn{g?jhMqT)uLYc%7UJolw)%2{Lsj&zGat?WvoZy})qCq;+3LaN zwob6kfWbSMeSciiNBf(--Ld*Hyu7pv2kbS^ww z<;uAJGuxNLM}2~et(G|$bWiz~!zf(4Z~m^ir&%keczm4Mo&DtxciNd<$M>Ap-(SSS z^45ONrjOr?&iijqwqSjg*7!MS!u$_Dj9YX6-aGpJquqj^ytz(hw^p&qSnjR+V)IMY z|GzTh_HRtF}z@Qjt3y88X=<}%km8s*KcK5P0*Ik@wP!-)rX zz6ADWh|imU_};@kf~{5=*-Y=&PPv_Ic2JnbNch|2`L>f;Twh8`wR9wCJ#Tow^0tl4 zgV_RK0-09H-n;j6R=CjHIQ>UI%l565DVI6g9Pr<8wNFySGt<`(judSBzbIhyGZ8OM z@%j5&76w;b@wu`tynFY&&hO`r?7Xou!$xL%vA}9m@9-oet@Uny9)*?g1X<@UH){P; zV0QM&+o|c|zY=e}H8}O?Zn6DG5w;2uhW&}_6<$Te`%77I$9zewd9jJ_bI!Yy5BV27 zJ$5oKq3WG9dz&}6-k+KRDKD#k@(pJS-4-UahAgX)y{qAuSTk4Z^wr1Tn;37(#(Cy& z9o$&IdDW&>PcQte7iIl&DSaV}z;wf7`WeZOnpwR3Y@Awc9&O)w85vnw84Pj^1q}Gum_u3E zgxN#$vh(uG^I#$z7$WQ#B3u|E+!!Lva1jFqab80s14}~#Lkj~lQ;R4Gej`J$fFV?X z&hBYqR6-6AMpg#qCPsb+gC<5UrY1&4h9fIFdgCu-+DNasYf-m*jov zzjJBQJ$1S5j}1Q_J5jyG*sIH3-y`jQRlxfFZb281_+%`X+(6}N8?G?xz2SoXD=quV}h$+?%eZUu-liuvj{ z?tdt{Pu=B%wc~=gSsVB9vKEUy-M#rj@bAKt@{crpCG?E~J5QaNd}+rAabZ4P))m(l z>N)RdGyS1>Q&3vg)?fVMg^04{?`DVD&ehR9dT*-T>)4g6Z`!PW%+VR-yuV~a$+z2C enUC`gQ+7*Af9CJ(+Qv73;lhr0XKtlhW&r@GI>*%j literal 0 HcmV?d00001 diff --git a/engine/src/flutter/build/android/ant/empty/res/.keep b/engine/src/flutter/build/android/ant/empty/res/.keep new file mode 100644 index 0000000000..1fd038b8cf --- /dev/null +++ b/engine/src/flutter/build/android/ant/empty/res/.keep @@ -0,0 +1,2 @@ +# This empty res folder can be passed to aapt while building Java libraries or +# APKs that don't have any resources. diff --git a/engine/src/flutter/build/android/asan_symbolize.py b/engine/src/flutter/build/android/asan_symbolize.py new file mode 100755 index 0000000000..10087a637a --- /dev/null +++ b/engine/src/flutter/build/android/asan_symbolize.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# +# Copyright 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. + + +import collections +import optparse +import os +import re +import sys + +from pylib import constants + +# Uses symbol.py from third_party/android_platform, not python's. +sys.path.insert(0, + os.path.join(constants.DIR_SOURCE_ROOT, + 'third_party/android_platform/development/scripts')) +import symbol + + +_RE_ASAN = re.compile(r'(.*?)(#\S*?) (\S*?) \((.*?)\+(.*?)\)') + +def _ParseAsanLogLine(line): + m = re.match(_RE_ASAN, line) + if not m: + return None + return { + 'prefix': m.group(1), + 'library': m.group(4), + 'pos': m.group(2), + 'rel_address': '%08x' % int(m.group(5), 16), + } + + +def _FindASanLibraries(): + asan_lib_dir = os.path.join(constants.DIR_SOURCE_ROOT, + 'third_party', 'llvm-build', + 'Release+Asserts', 'lib') + asan_libs = [] + for src_dir, _, files in os.walk(asan_lib_dir): + asan_libs += [os.path.relpath(os.path.join(src_dir, f)) + for f in files + if f.endswith('.so')] + return asan_libs + + +def _TranslateLibPath(library, asan_libs): + for asan_lib in asan_libs: + if os.path.basename(library) == os.path.basename(asan_lib): + return '/' + asan_lib + return symbol.TranslateLibPath(library) + + +def _Symbolize(asan_input): + asan_libs = _FindASanLibraries() + libraries = collections.defaultdict(list) + asan_lines = [] + for asan_log_line in [a.rstrip() for a in asan_input]: + m = _ParseAsanLogLine(asan_log_line) + if m: + libraries[m['library']].append(m) + asan_lines.append({'raw_log': asan_log_line, 'parsed': m}) + + all_symbols = collections.defaultdict(dict) + for library, items in libraries.iteritems(): + libname = _TranslateLibPath(library, asan_libs) + lib_relative_addrs = set([i['rel_address'] for i in items]) + info_dict = symbol.SymbolInformationForSet(libname, + lib_relative_addrs, + True) + if info_dict: + all_symbols[library]['symbols'] = info_dict + + for asan_log_line in asan_lines: + m = asan_log_line['parsed'] + if not m: + print asan_log_line['raw_log'] + continue + if (m['library'] in all_symbols and + m['rel_address'] in all_symbols[m['library']]['symbols']): + s = all_symbols[m['library']]['symbols'][m['rel_address']][0] + print '%s%s %s %s' % (m['prefix'], m['pos'], s[0], s[1]) + else: + print asan_log_line['raw_log'] + + +def main(): + parser = optparse.OptionParser() + parser.add_option('-l', '--logcat', + help='File containing adb logcat output with ASan stacks. ' + 'Use stdin if not specified.') + options, _ = parser.parse_args() + if options.logcat: + asan_input = file(options.logcat, 'r') + else: + asan_input = sys.stdin + _Symbolize(asan_input.readlines()) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/engine/src/flutter/build/android/avd.py b/engine/src/flutter/build/android/avd.py new file mode 100755 index 0000000000..c45544f8bc --- /dev/null +++ b/engine/src/flutter/build/android/avd.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# 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. + +"""Launches Android Virtual Devices with a set configuration for testing Chrome. + +The script will launch a specified number of Android Virtual Devices (AVD's). +""" + + +import install_emulator_deps +import logging +import optparse +import os +import re +import sys + +from pylib import cmd_helper +from pylib import constants +from pylib.utils import emulator + + +def main(argv): + # ANDROID_SDK_ROOT needs to be set to the location of the SDK used to launch + # the emulator to find the system images upon launch. + emulator_sdk = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk') + os.environ['ANDROID_SDK_ROOT'] = emulator_sdk + + opt_parser = optparse.OptionParser(description='AVD script.') + opt_parser.add_option('--name', help='Optinaly, name of existing AVD to ' + 'launch. If not specified, new AVD\'s will be created') + opt_parser.add_option('-n', '--num', dest='emulator_count', + help='Number of emulators to launch (default is 1).', + type='int', default='1') + opt_parser.add_option('--abi', default='x86', + help='Platform of emulators to launch (x86 default).') + opt_parser.add_option('--api-level', dest='api_level', + help='API level for the image, e.g. 19 for Android 4.4', + type='int', default=constants.ANDROID_SDK_VERSION) + + options, _ = opt_parser.parse_args(argv[1:]) + + logging.basicConfig(level=logging.INFO, + format='# %(asctime)-15s: %(message)s') + logging.root.setLevel(logging.INFO) + + # Check if KVM is enabled for x86 AVD's and check for x86 system images. + # TODO(andrewhayden) Since we can fix all of these with install_emulator_deps + # why don't we just run it? + if options.abi == 'x86': + if not install_emulator_deps.CheckKVM(): + logging.critical('ERROR: KVM must be enabled in BIOS, and installed. ' + 'Enable KVM in BIOS and run install_emulator_deps.py') + return 1 + elif not install_emulator_deps.CheckX86Image(options.api_level): + logging.critical('ERROR: System image for x86 AVD not installed. Run ' + 'install_emulator_deps.py') + return 1 + + if not install_emulator_deps.CheckSDK(): + logging.critical('ERROR: Emulator SDK not installed. Run ' + 'install_emulator_deps.py.') + return 1 + + # If AVD is specified, check that the SDK has the required target. If not, + # check that the SDK has the desired target for the temporary AVD's. + api_level = options.api_level + if options.name: + android = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk', 'tools', + 'android') + avds_output = cmd_helper.GetCmdOutput([android, 'list', 'avd']) + names = re.findall(r'Name: (\w+)', avds_output) + api_levels = re.findall(r'API level (\d+)', avds_output) + try: + avd_index = names.index(options.name) + except ValueError: + logging.critical('ERROR: Specified AVD %s does not exist.' % options.name) + return 1 + api_level = int(api_levels[avd_index]) + + if not install_emulator_deps.CheckSDKPlatform(api_level): + logging.critical('ERROR: Emulator SDK missing required target for API %d. ' + 'Run install_emulator_deps.py.') + return 1 + + if options.name: + emulator.LaunchEmulator(options.name, options.abi) + else: + emulator.LaunchTempEmulators(options.emulator_count, options.abi, + options.api_level, True) + + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/bb_run_sharded_steps.py b/engine/src/flutter/build/android/bb_run_sharded_steps.py new file mode 100755 index 0000000000..6aeba5b5f5 --- /dev/null +++ b/engine/src/flutter/build/android/bb_run_sharded_steps.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 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. + +"""DEPRECATED! +TODO(bulach): remove me once all other repositories reference +'test_runner.py perf' directly. +""" + +import optparse +import sys + +from pylib import cmd_helper + + +def main(argv): + parser = optparse.OptionParser() + parser.add_option('-s', '--steps', + help='A JSON file containing all the steps to be ' + 'sharded.') + parser.add_option('--flaky_steps', + help='A JSON file containing steps that are flaky and ' + 'will have its exit code ignored.') + parser.add_option('-p', '--print_results', + help='Only prints the results for the previously ' + 'executed step, do not run it again.') + options, _ = parser.parse_args(argv) + if options.print_results: + return cmd_helper.RunCmd(['build/android/test_runner.py', 'perf', + '--print-step', options.print_results]) + flaky_options = [] + if options.flaky_steps: + flaky_options = ['--flaky-steps', options.flaky_steps] + return cmd_helper.RunCmd(['build/android/test_runner.py', 'perf', '-v', + '--steps', options.steps] + flaky_options) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/buildbot/OWNERS b/engine/src/flutter/build/android/buildbot/OWNERS new file mode 100644 index 0000000000..f289720120 --- /dev/null +++ b/engine/src/flutter/build/android/buildbot/OWNERS @@ -0,0 +1,6 @@ +set noparent + +cmp@chromium.org +jbudorick@chromium.org +navabi@chromium.org + diff --git a/engine/src/flutter/build/android/buildbot/bb_annotations.py b/engine/src/flutter/build/android/buildbot/bb_annotations.py new file mode 100644 index 0000000000..059d673188 --- /dev/null +++ b/engine/src/flutter/build/android/buildbot/bb_annotations.py @@ -0,0 +1,46 @@ +# Copyright 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. + +"""Helper functions to print buildbot messages.""" + +def PrintLink(label, url): + """Adds a link with name |label| linking to |url| to current buildbot step. + + Args: + label: A string with the name of the label. + url: A string of the URL. + """ + print '@@@STEP_LINK@%s@%s@@@' % (label, url) + + +def PrintMsg(msg): + """Appends |msg| to the current buildbot step text. + + Args: + msg: String to be appended. + """ + print '@@@STEP_TEXT@%s@@@' % msg + + +def PrintSummaryText(msg): + """Appends |msg| to main build summary. Visible from waterfall. + + Args: + msg: String to be appended. + """ + print '@@@STEP_SUMMARY_TEXT@%s@@@' % msg + + +def PrintError(): + """Marks the current step as failed.""" + print '@@@STEP_FAILURE@@@' + + +def PrintWarning(): + """Marks the current step with a warning.""" + print '@@@STEP_WARNINGS@@@' + + +def PrintNamedStep(step): + print '@@@BUILD_STEP %s@@@' % step diff --git a/engine/src/flutter/build/android/buildbot/bb_device_status_check.py b/engine/src/flutter/build/android/buildbot/bb_device_status_check.py new file mode 100755 index 0000000000..917c51e28d --- /dev/null +++ b/engine/src/flutter/build/android/buildbot/bb_device_status_check.py @@ -0,0 +1,404 @@ +#!/usr/bin/env python +# +# Copyright 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 class to keep track of devices across builds and report state.""" +import json +import logging +import optparse +import os +import psutil +import re +import signal +import smtplib +import subprocess +import sys +import time +import urllib + +import bb_annotations +import bb_utils + +sys.path.append(os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir, 'util', 'lib', + 'common')) +import perf_tests_results_helper # pylint: disable=F0401 + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from pylib import constants +from pylib.cmd_helper import GetCmdOutput +from pylib.device import adb_wrapper +from pylib.device import battery_utils +from pylib.device import device_blacklist +from pylib.device import device_errors +from pylib.device import device_list +from pylib.device import device_utils +from pylib.utils import run_tests_helper + +_RE_DEVICE_ID = re.compile('Device ID = (\d+)') + +def DeviceInfo(device, options): + """Gathers info on a device via various adb calls. + + Args: + device: A DeviceUtils instance for the device to construct info about. + + Returns: + Tuple of device type, build id, report as a string, error messages, and + boolean indicating whether or not device can be used for testing. + """ + battery = battery_utils.BatteryUtils(device) + + build_product = '' + build_id = '' + battery_level = 100 + errors = [] + dev_good = True + json_data = {} + + try: + build_product = device.build_product + build_id = device.build_id + + json_data = { + 'serial': device.adb.GetDeviceSerial(), + 'type': build_product, + 'build': build_id, + 'build_detail': device.GetProp('ro.build.fingerprint'), + 'battery': {}, + 'imei_slice': 'Unknown', + 'wifi_ip': device.GetProp('dhcp.wlan0.ipaddress'), + } + + battery_info = {} + try: + battery_info = battery.GetBatteryInfo(timeout=5) + battery_level = int(battery_info.get('level', battery_level)) + json_data['battery'] = battery_info + except device_errors.CommandFailedError: + logging.exception('Failed to get battery information for %s', str(device)) + + try: + for l in device.RunShellCommand(['dumpsys', 'iphonesubinfo'], + check_return=True, timeout=5): + m = _RE_DEVICE_ID.match(l) + if m: + json_data['imei_slice'] = m.group(1)[-6:] + except device_errors.CommandFailedError: + logging.exception('Failed to get IMEI slice for %s', str(device)) + + if battery_level < 15: + errors += ['Device critically low in battery.'] + dev_good = False + if not battery.GetCharging(): + battery.SetCharging(True) + if not options.no_provisioning_check: + setup_wizard_disabled = ( + device.GetProp('ro.setupwizard.mode') == 'DISABLED') + if not setup_wizard_disabled and device.build_type != 'user': + errors += ['Setup wizard not disabled. Was it provisioned correctly?'] + if (device.product_name == 'mantaray' and + battery_info.get('AC powered', None) != 'true'): + errors += ['Mantaray device not connected to AC power.'] + except device_errors.CommandFailedError: + logging.exception('Failure while getting device status.') + dev_good = False + except device_errors.CommandTimeoutError: + logging.exception('Timeout while getting device status.') + dev_good = False + + return (build_product, build_id, battery_level, errors, dev_good, json_data) + + +def CheckForMissingDevices(options, devices): + """Uses file of previous online devices to detect broken phones. + + Args: + options: out_dir parameter of options argument is used as the base + directory to load and update the cache file. + devices: A list of DeviceUtils instance for the currently visible and + online attached devices. + """ + out_dir = os.path.abspath(options.out_dir) + device_serials = set(d.adb.GetDeviceSerial() for d in devices) + + # last_devices denotes all known devices prior to this run + last_devices_path = os.path.join(out_dir, device_list.LAST_DEVICES_FILENAME) + last_missing_devices_path = os.path.join(out_dir, + device_list.LAST_MISSING_DEVICES_FILENAME) + try: + last_devices = device_list.GetPersistentDeviceList(last_devices_path) + except IOError: + # Ignore error, file might not exist + last_devices = [] + + try: + last_missing_devices = device_list.GetPersistentDeviceList( + last_missing_devices_path) + except IOError: + last_missing_devices = [] + + missing_devs = list(set(last_devices) - device_serials) + new_missing_devs = list(set(missing_devs) - set(last_missing_devices)) + + if new_missing_devs and os.environ.get('BUILDBOT_SLAVENAME'): + logging.info('new_missing_devs %s' % new_missing_devs) + devices_missing_msg = '%d devices not detected.' % len(missing_devs) + bb_annotations.PrintSummaryText(devices_missing_msg) + + from_address = 'chrome-bot@chromium.org' + to_addresses = ['chrome-labs-tech-ticket@google.com', + 'chrome-android-device-alert@google.com'] + cc_addresses = ['chrome-android-device-alert@google.com'] + subject = 'Devices offline on %s, %s, %s' % ( + os.environ.get('BUILDBOT_SLAVENAME'), + os.environ.get('BUILDBOT_BUILDERNAME'), + os.environ.get('BUILDBOT_BUILDNUMBER')) + msg = ('Please reboot the following devices:\n%s' % + '\n'.join(map(str, new_missing_devs))) + SendEmail(from_address, to_addresses, cc_addresses, subject, msg) + + all_known_devices = list(device_serials | set(last_devices)) + device_list.WritePersistentDeviceList(last_devices_path, all_known_devices) + device_list.WritePersistentDeviceList(last_missing_devices_path, missing_devs) + + if not all_known_devices: + # This can happen if for some reason the .last_devices file is not + # present or if it was empty. + return ['No online devices. Have any devices been plugged in?'] + if missing_devs: + devices_missing_msg = '%d devices not detected.' % len(missing_devs) + bb_annotations.PrintSummaryText(devices_missing_msg) + return ['Current online devices: %s' % ', '.join(d for d in device_serials), + '%s are no longer visible. Were they removed?' % missing_devs] + else: + new_devs = device_serials - set(last_devices) + if new_devs and os.path.exists(last_devices_path): + bb_annotations.PrintWarning() + bb_annotations.PrintSummaryText( + '%d new devices detected' % len(new_devs)) + logging.info('New devices detected:') + for d in new_devs: + logging.info(' %s', d) + + +def SendEmail(from_address, to_addresses, cc_addresses, subject, msg): + msg_body = '\r\n'.join(['From: %s' % from_address, + 'To: %s' % ', '.join(to_addresses), + 'CC: %s' % ', '.join(cc_addresses), + 'Subject: %s' % subject, '', msg]) + try: + server = smtplib.SMTP('localhost') + server.sendmail(from_address, to_addresses, msg_body) + server.quit() + except Exception: + logging.exception('Failed to send alert email.') + + +def RestartUsb(): + if not os.path.isfile('/usr/bin/restart_usb'): + logging.error('Could not restart usb. ''/usr/bin/restart_usb not ' + 'installed on host (see BUG=305769).') + return False + + lsusb_proc = bb_utils.SpawnCmd(['lsusb'], stdout=subprocess.PIPE) + lsusb_output, _ = lsusb_proc.communicate() + if lsusb_proc.returncode: + logging.error('Could not get list of USB ports (i.e. lsusb).') + return lsusb_proc.returncode + + usb_devices = [re.findall(r'Bus (\d\d\d) Device (\d\d\d)', lsusb_line)[0] + for lsusb_line in lsusb_output.strip().split('\n')] + + all_restarted = True + # Walk USB devices from leaves up (i.e reverse sorted) restarting the + # connection. If a parent node (e.g. usb hub) is restarted before the + # devices connected to it, the (bus, dev) for the hub can change, making the + # output we have wrong. This way we restart the devices before the hub. + for (bus, dev) in reversed(sorted(usb_devices)): + # Can not restart root usb connections + if dev != '001': + return_code = bb_utils.RunCmd(['/usr/bin/restart_usb', bus, dev]) + if return_code: + logging.error('Error restarting USB device /dev/bus/usb/%s/%s', + bus, dev) + all_restarted = False + else: + logging.info('Restarted USB device /dev/bus/usb/%s/%s', bus, dev) + + return all_restarted + + +def KillAllAdb(): + def GetAllAdb(): + for p in psutil.process_iter(): + try: + if 'adb' in p.name: + yield p + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + for sig in [signal.SIGTERM, signal.SIGQUIT, signal.SIGKILL]: + for p in GetAllAdb(): + try: + logging.info('kill %d %d (%s [%s])', sig, p.pid, p.name, + ' '.join(p.cmdline)) + p.send_signal(sig) + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + for p in GetAllAdb(): + try: + logging.error('Unable to kill %d (%s [%s])', p.pid, p.name, + ' '.join(p.cmdline)) + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + +def main(): + parser = optparse.OptionParser() + parser.add_option('', '--out-dir', + help='Directory where the device path is stored', + default=os.path.join(constants.DIR_SOURCE_ROOT, 'out')) + parser.add_option('--no-provisioning-check', action='store_true', + help='Will not check if devices are provisioned properly.') + parser.add_option('--device-status-dashboard', action='store_true', + help='Output device status data for dashboard.') + parser.add_option('--restart-usb', action='store_true', + help='Restart USB ports before running device check.') + parser.add_option('--json-output', + help='Output JSON information into a specified file.') + parser.add_option('-v', '--verbose', action='count', default=1, + help='Log more information.') + + options, args = parser.parse_args() + if args: + parser.error('Unknown options %s' % args) + + run_tests_helper.SetLogLevel(options.verbose) + + # Remove the last build's "bad devices" before checking device statuses. + device_blacklist.ResetBlacklist() + + try: + expected_devices = device_list.GetPersistentDeviceList( + os.path.join(options.out_dir, device_list.LAST_DEVICES_FILENAME)) + except IOError: + expected_devices = [] + devices = device_utils.DeviceUtils.HealthyDevices() + device_serials = [d.adb.GetDeviceSerial() for d in devices] + # Only restart usb if devices are missing. + if set(expected_devices) != set(device_serials): + logging.warning('expected_devices: %s', expected_devices) + logging.warning('devices: %s', device_serials) + KillAllAdb() + retries = 5 + usb_restarted = True + if options.restart_usb: + if not RestartUsb(): + usb_restarted = False + bb_annotations.PrintWarning() + logging.error('USB reset stage failed, ' + 'wait for any device to come back.') + while retries: + logging.info('retry adb devices...') + time.sleep(1) + devices = device_utils.DeviceUtils.HealthyDevices() + device_serials = [d.adb.GetDeviceSerial() for d in devices] + if set(expected_devices) == set(device_serials): + # All devices are online, keep going. + break + if not usb_restarted and devices: + # The USB wasn't restarted, but there's at least one device online. + # No point in trying to wait for all devices. + break + retries -= 1 + + types, builds, batteries, errors, devices_ok, json_data = ( + [], [], [], [], [], []) + if devices: + types, builds, batteries, errors, devices_ok, json_data = ( + zip(*[DeviceInfo(dev, options) for dev in devices])) + + # Write device info to file for buildbot info display. + if os.path.exists('/home/chrome-bot'): + with open('/home/chrome-bot/.adb_device_info', 'w') as f: + for device in json_data: + try: + f.write('%s %s %s %.1fC %s%%\n' % (device['serial'], device['type'], + device['build'], float(device['battery']['temperature']) / 10, + device['battery']['level'])) + except Exception: + pass + + err_msg = CheckForMissingDevices(options, devices) or [] + + unique_types = list(set(types)) + unique_builds = list(set(builds)) + + bb_annotations.PrintMsg('Online devices: %d. Device types %s, builds %s' + % (len(devices), unique_types, unique_builds)) + + for j in json_data: + logging.info('Device %s (%s)', j.get('serial'), j.get('type')) + logging.info(' Build: %s (%s)', j.get('build'), j.get('build_detail')) + logging.info(' Current Battery Service state:') + for k, v in j.get('battery', {}).iteritems(): + logging.info(' %s: %s', k, v) + logging.info(' IMEI slice: %s', j.get('imei_slice')) + logging.info(' WiFi IP: %s', j.get('wifi_ip')) + + + for dev, dev_errors in zip(devices, errors): + if dev_errors: + err_msg += ['%s errors:' % str(dev)] + err_msg += [' %s' % error for error in dev_errors] + + if err_msg: + bb_annotations.PrintWarning() + for e in err_msg: + logging.error(e) + from_address = 'buildbot@chromium.org' + to_addresses = ['chromium-android-device-alerts@google.com'] + bot_name = os.environ.get('BUILDBOT_BUILDERNAME') + slave_name = os.environ.get('BUILDBOT_SLAVENAME') + subject = 'Device status check errors on %s, %s.' % (slave_name, bot_name) + SendEmail(from_address, to_addresses, [], subject, '\n'.join(err_msg)) + + if options.device_status_dashboard: + offline_devices = [ + device_utils.DeviceUtils(a) + for a in adb_wrapper.AdbWrapper.Devices(is_ready=False) + if a.GetState() == 'offline'] + + perf_tests_results_helper.PrintPerfResult('BotDevices', 'OnlineDevices', + [len(devices)], 'devices') + perf_tests_results_helper.PrintPerfResult('BotDevices', 'OfflineDevices', + [len(offline_devices)], 'devices', + 'unimportant') + for dev, battery in zip(devices, batteries): + perf_tests_results_helper.PrintPerfResult('DeviceBattery', str(dev), + [battery], '%', + 'unimportant') + + if options.json_output: + with open(options.json_output, 'wb') as f: + f.write(json.dumps(json_data, indent=4)) + + num_failed_devs = 0 + for device_ok, device in zip(devices_ok, devices): + if not device_ok: + logging.warning('Blacklisting %s', str(device)) + device_blacklist.ExtendBlacklist([str(device)]) + num_failed_devs += 1 + + if num_failed_devs == len(devices): + return 2 + + if not devices: + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/buildbot/bb_device_steps.py b/engine/src/flutter/build/android/buildbot/bb_device_steps.py new file mode 100755 index 0000000000..8ad42b90d1 --- /dev/null +++ b/engine/src/flutter/build/android/buildbot/bb_device_steps.py @@ -0,0 +1,796 @@ +#!/usr/bin/env python +# 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. + +import collections +import glob +import hashlib +import json +import os +import random +import re +import shutil +import sys + +import bb_utils +import bb_annotations + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import provision_devices +from pylib import constants +from pylib.device import device_utils +from pylib.gtest import gtest_config + +CHROME_SRC_DIR = bb_utils.CHROME_SRC +DIR_BUILD_ROOT = os.path.dirname(CHROME_SRC_DIR) +CHROME_OUT_DIR = bb_utils.CHROME_OUT_DIR +BLINK_SCRIPTS_DIR = 'third_party/WebKit/Tools/Scripts' + +SLAVE_SCRIPTS_DIR = os.path.join(bb_utils.BB_BUILD_DIR, 'scripts', 'slave') +LOGCAT_DIR = os.path.join(bb_utils.CHROME_OUT_DIR, 'logcat') +GS_URL = 'https://storage.googleapis.com' +GS_AUTH_URL = 'https://storage.cloud.google.com' + +# Describes an instrumation test suite: +# test: Name of test we're running. +# apk: apk to be installed. +# apk_package: package for the apk to be installed. +# test_apk: apk to run tests on. +# test_data: data folder in format destination:source. +# host_driven_root: The host-driven test root directory. +# annotation: Annotation of the tests to include. +# exclude_annotation: The annotation of the tests to exclude. +I_TEST = collections.namedtuple('InstrumentationTest', [ + 'name', 'apk', 'apk_package', 'test_apk', 'test_data', 'isolate_file_path', + 'host_driven_root', 'annotation', 'exclude_annotation', 'extra_flags']) + + +def SrcPath(*path): + return os.path.join(CHROME_SRC_DIR, *path) + + +def I(name, apk, apk_package, test_apk, test_data, isolate_file_path=None, + host_driven_root=None, annotation=None, exclude_annotation=None, + extra_flags=None): + return I_TEST(name, apk, apk_package, test_apk, test_data, isolate_file_path, + host_driven_root, annotation, exclude_annotation, extra_flags) + +INSTRUMENTATION_TESTS = dict((suite.name, suite) for suite in [ + I('ContentShell', + 'ContentShell.apk', + 'org.chromium.content_shell_apk', + 'ContentShellTest', + 'content:content/test/data/android/device_files', + isolate_file_path='content/content_shell_test_apk.isolate'), + I('ChromeShell', + 'ChromeShell.apk', + 'org.chromium.chrome.shell', + 'ChromeShellTest', + 'chrome:chrome/test/data/android/device_files', + isolate_file_path='chrome/chrome_shell_test_apk.isolate', + host_driven_root=constants.CHROME_SHELL_HOST_DRIVEN_DIR), + I('AndroidWebView', + 'AndroidWebView.apk', + 'org.chromium.android_webview.shell', + 'AndroidWebViewTest', + 'webview:android_webview/test/data/device_files', + isolate_file_path='android_webview/android_webview_test_apk.isolate'), + I('ChromeSyncShell', + 'ChromeSyncShell.apk', + 'org.chromium.chrome.browser.sync', + 'ChromeSyncShellTest', + None), + ]) + +InstallablePackage = collections.namedtuple('InstallablePackage', [ + 'name', 'apk', 'apk_package']) + +INSTALLABLE_PACKAGES = dict((package.name, package) for package in ( + [InstallablePackage(i.name, i.apk, i.apk_package) + for i in INSTRUMENTATION_TESTS.itervalues()] + + [InstallablePackage('ChromeDriverWebViewShell', + 'ChromeDriverWebViewShell.apk', + 'org.chromium.chromedriver_webview_shell')])) + +VALID_TESTS = set([ + 'base_junit_tests', + 'chromedriver', + 'chrome_proxy', + 'components_browsertests', + 'gfx_unittests', + 'gl_unittests', + 'gpu', + 'python_unittests', + 'telemetry_unittests', + 'telemetry_perf_unittests', + 'ui', + 'unit', + 'webkit', + 'webkit_layout' +]) + +RunCmd = bb_utils.RunCmd + + +def _GetRevision(options): + """Get the SVN revision number. + + Args: + options: options object. + + Returns: + The revision number. + """ + revision = options.build_properties.get('got_revision') + if not revision: + revision = options.build_properties.get('revision', 'testing') + return revision + + +def _RunTest(options, cmd, suite): + """Run test command with runtest.py. + + Args: + options: options object. + cmd: the command to run. + suite: test name. + """ + property_args = bb_utils.EncodeProperties(options) + args = [os.path.join(SLAVE_SCRIPTS_DIR, 'runtest.py')] + property_args + args += ['--test-platform', 'android'] + if options.factory_properties.get('generate_gtest_json'): + args.append('--generate-json-file') + args += ['-o', 'gtest-results/%s' % suite, + '--annotate', 'gtest', + '--build-number', str(options.build_properties.get('buildnumber', + '')), + '--builder-name', options.build_properties.get('buildername', '')] + if options.target == 'Release': + args += ['--target', 'Release'] + else: + args += ['--target', 'Debug'] + if options.flakiness_server: + args += ['--flakiness-dashboard-server=%s' % + options.flakiness_server] + args += cmd + RunCmd(args, cwd=DIR_BUILD_ROOT) + + +def RunTestSuites(options, suites, suites_options=None): + """Manages an invocation of test_runner.py for gtests. + + Args: + options: options object. + suites: List of suite names to run. + suites_options: Command line options dictionary for particular suites. + For example, + {'content_browsertests', ['--num_retries=1', '--release']} + will add the options only to content_browsertests. + """ + + if not suites_options: + suites_options = {} + + args = ['--verbose'] + if options.target == 'Release': + args.append('--release') + if options.asan: + args.append('--tool=asan') + if options.gtest_filter: + args.append('--gtest-filter=%s' % options.gtest_filter) + + for suite in suites: + bb_annotations.PrintNamedStep(suite) + cmd = [suite] + args + cmd += suites_options.get(suite, []) + if suite == 'content_browsertests' or suite == 'components_browsertests': + cmd.append('--num_retries=1') + _RunTest(options, cmd, suite) + + +def RunJunitSuite(suite): + bb_annotations.PrintNamedStep(suite) + RunCmd(['build/android/test_runner.py', 'junit', '-s', suite]) + + +def RunChromeDriverTests(options): + """Run all the steps for running chromedriver tests.""" + bb_annotations.PrintNamedStep('chromedriver_annotation') + RunCmd(['chrome/test/chromedriver/run_buildbot_steps.py', + '--android-packages=%s,%s,%s,%s' % + ('chrome_shell', + 'chrome_stable', + 'chrome_beta', + 'chromedriver_webview_shell'), + '--revision=%s' % _GetRevision(options), + '--update-log']) + +def RunChromeProxyTests(options): + """Run the chrome_proxy tests. + + Args: + options: options object. + """ + InstallApk(options, INSTRUMENTATION_TESTS['ChromeShell'], False) + args = ['--browser', 'android-chrome-shell'] + devices = device_utils.DeviceUtils.HealthyDevices() + if devices: + args = args + ['--device', devices[0].adb.GetDeviceSerial()] + bb_annotations.PrintNamedStep('chrome_proxy') + RunCmd(['tools/chrome_proxy/run_tests'] + args) + + +def RunTelemetryTests(options, step_name, run_tests_path): + """Runs either telemetry_perf_unittests or telemetry_unittests. + + Args: + options: options object. + step_name: either 'telemetry_unittests' or 'telemetry_perf_unittests' + run_tests_path: path to run_tests script (tools/perf/run_tests for + perf_unittests and tools/telemetry/run_tests for + telemetry_unittests) + """ + InstallApk(options, INSTRUMENTATION_TESTS['ChromeShell'], False) + args = ['--browser', 'android-chrome-shell'] + devices = device_utils.DeviceUtils.HealthyDevices() + if devices: + args = args + ['--device', 'android'] + bb_annotations.PrintNamedStep(step_name) + RunCmd([run_tests_path] + args) + + +def InstallApk(options, test, print_step=False): + """Install an apk to all phones. + + Args: + options: options object + test: An I_TEST namedtuple + print_step: Print a buildbot step + """ + if print_step: + bb_annotations.PrintNamedStep('install_%s' % test.name.lower()) + + args = ['--apk_package', test.apk_package] + if options.target == 'Release': + args.append('--release') + args.append(test.apk) + + RunCmd(['build/android/adb_install_apk.py'] + args, halt_on_failure=True) + + +def RunInstrumentationSuite(options, test, flunk_on_failure=True, + python_only=False, official_build=False): + """Manages an invocation of test_runner.py for instrumentation tests. + + Args: + options: options object + test: An I_TEST namedtuple + flunk_on_failure: Flunk the step if tests fail. + Python: Run only host driven Python tests. + official_build: Run official-build tests. + """ + bb_annotations.PrintNamedStep('%s_instrumentation_tests' % test.name.lower()) + + if test.apk: + InstallApk(options, test) + args = ['--test-apk', test.test_apk, '--verbose'] + if test.test_data: + args.extend(['--test_data', test.test_data]) + if options.target == 'Release': + args.append('--release') + if options.asan: + args.append('--tool=asan') + if options.flakiness_server: + args.append('--flakiness-dashboard-server=%s' % + options.flakiness_server) + if options.coverage_bucket: + args.append('--coverage-dir=%s' % options.coverage_dir) + if test.isolate_file_path: + args.append('--isolate-file-path=%s' % test.isolate_file_path) + if test.host_driven_root: + args.append('--host-driven-root=%s' % test.host_driven_root) + if test.annotation: + args.extend(['-A', test.annotation]) + if test.exclude_annotation: + args.extend(['-E', test.exclude_annotation]) + if test.extra_flags: + args.extend(test.extra_flags) + if python_only: + args.append('-p') + if official_build: + # The option needs to be assigned 'True' as it does not have an action + # associated with it. + args.append('--official-build') + + RunCmd(['build/android/test_runner.py', 'instrumentation'] + args, + flunk_on_failure=flunk_on_failure) + + +def RunWebkitLint(): + """Lint WebKit's TestExpectation files.""" + bb_annotations.PrintNamedStep('webkit_lint') + RunCmd([SrcPath(os.path.join(BLINK_SCRIPTS_DIR, 'lint-test-expectations'))]) + + +def RunWebkitLayoutTests(options): + """Run layout tests on an actual device.""" + bb_annotations.PrintNamedStep('webkit_tests') + cmd_args = [ + '--no-show-results', + '--no-new-test-results', + '--full-results-html', + '--clobber-old-results', + '--exit-after-n-failures', '5000', + '--exit-after-n-crashes-or-timeouts', '100', + '--debug-rwt-logging', + '--results-directory', '../layout-test-results', + '--target', options.target, + '--builder-name', options.build_properties.get('buildername', ''), + '--build-number', str(options.build_properties.get('buildnumber', '')), + '--master-name', 'ChromiumWebkit', # TODO: Get this from the cfg. + '--build-name', options.build_properties.get('buildername', ''), + '--platform=android'] + + for flag in 'test_results_server', 'driver_name', 'additional_driver_flag': + if flag in options.factory_properties: + cmd_args.extend(['--%s' % flag.replace('_', '-'), + options.factory_properties.get(flag)]) + + for f in options.factory_properties.get('additional_expectations', []): + cmd_args.extend( + ['--additional-expectations=%s' % os.path.join(CHROME_SRC_DIR, *f)]) + + # TODO(dpranke): Remove this block after + # https://codereview.chromium.org/12927002/ lands. + for f in options.factory_properties.get('additional_expectations_files', []): + cmd_args.extend( + ['--additional-expectations=%s' % os.path.join(CHROME_SRC_DIR, *f)]) + + exit_code = RunCmd( + [SrcPath(os.path.join(BLINK_SCRIPTS_DIR, 'run-webkit-tests'))] + cmd_args) + if exit_code == 255: # test_run_results.UNEXPECTED_ERROR_EXIT_STATUS + bb_annotations.PrintMsg('?? (crashed or hung)') + elif exit_code == 254: # test_run_results.NO_DEVICES_EXIT_STATUS + bb_annotations.PrintMsg('?? (no devices found)') + elif exit_code == 253: # test_run_results.NO_TESTS_EXIT_STATUS + bb_annotations.PrintMsg('?? (no tests found)') + else: + full_results_path = os.path.join('..', 'layout-test-results', + 'full_results.json') + if os.path.exists(full_results_path): + full_results = json.load(open(full_results_path)) + unexpected_passes, unexpected_failures, unexpected_flakes = ( + _ParseLayoutTestResults(full_results)) + if unexpected_failures: + _PrintDashboardLink('failed', unexpected_failures.keys(), + max_tests=25) + elif unexpected_passes: + _PrintDashboardLink('unexpected passes', unexpected_passes.keys(), + max_tests=10) + if unexpected_flakes: + _PrintDashboardLink('unexpected flakes', unexpected_flakes.keys(), + max_tests=10) + + if exit_code == 0 and (unexpected_passes or unexpected_flakes): + # If exit_code != 0, RunCmd() will have already printed an error. + bb_annotations.PrintWarning() + else: + bb_annotations.PrintError() + bb_annotations.PrintMsg('?? (results missing)') + + if options.factory_properties.get('archive_webkit_results', False): + bb_annotations.PrintNamedStep('archive_webkit_results') + base = 'https://storage.googleapis.com/chromium-layout-test-archives' + builder_name = options.build_properties.get('buildername', '') + build_number = str(options.build_properties.get('buildnumber', '')) + results_link = '%s/%s/%s/layout-test-results/results.html' % ( + base, EscapeBuilderName(builder_name), build_number) + bb_annotations.PrintLink('results', results_link) + bb_annotations.PrintLink('(zip)', '%s/%s/%s/layout-test-results.zip' % ( + base, EscapeBuilderName(builder_name), build_number)) + gs_bucket = 'gs://chromium-layout-test-archives' + RunCmd([os.path.join(SLAVE_SCRIPTS_DIR, 'chromium', + 'archive_layout_test_results.py'), + '--results-dir', '../../layout-test-results', + '--build-number', build_number, + '--builder-name', builder_name, + '--gs-bucket', gs_bucket], + cwd=DIR_BUILD_ROOT) + + +def _ParseLayoutTestResults(results): + """Extract the failures from the test run.""" + # Cloned from third_party/WebKit/Tools/Scripts/print-json-test-results + tests = _ConvertTrieToFlatPaths(results['tests']) + failures = {} + flakes = {} + passes = {} + for (test, result) in tests.iteritems(): + if result.get('is_unexpected'): + actual_results = result['actual'].split() + expected_results = result['expected'].split() + if len(actual_results) > 1: + # We report the first failure type back, even if the second + # was more severe. + if actual_results[1] in expected_results: + flakes[test] = actual_results[0] + else: + failures[test] = actual_results[0] + elif actual_results[0] == 'PASS': + passes[test] = result + else: + failures[test] = actual_results[0] + + return (passes, failures, flakes) + + +def _ConvertTrieToFlatPaths(trie, prefix=None): + """Flatten the trie of failures into a list.""" + # Cloned from third_party/WebKit/Tools/Scripts/print-json-test-results + result = {} + for name, data in trie.iteritems(): + if prefix: + name = prefix + '/' + name + + if len(data) and 'actual' not in data and 'expected' not in data: + result.update(_ConvertTrieToFlatPaths(data, name)) + else: + result[name] = data + + return result + + +def _PrintDashboardLink(link_text, tests, max_tests): + """Add a link to the flakiness dashboard in the step annotations.""" + if len(tests) > max_tests: + test_list_text = ' '.join(tests[:max_tests]) + ' and more' + else: + test_list_text = ' '.join(tests) + + dashboard_base = ('http://test-results.appspot.com' + '/dashboards/flakiness_dashboard.html#' + 'master=ChromiumWebkit&tests=') + + bb_annotations.PrintLink('%d %s: %s' % + (len(tests), link_text, test_list_text), + dashboard_base + ','.join(tests)) + + +def EscapeBuilderName(builder_name): + return re.sub('[ ()]', '_', builder_name) + + +def SpawnLogcatMonitor(): + shutil.rmtree(LOGCAT_DIR, ignore_errors=True) + bb_utils.SpawnCmd([ + os.path.join(CHROME_SRC_DIR, 'build', 'android', 'adb_logcat_monitor.py'), + LOGCAT_DIR]) + + # Wait for logcat_monitor to pull existing logcat + RunCmd(['sleep', '5']) + + +def ProvisionDevices(options): + bb_annotations.PrintNamedStep('provision_devices') + + if not bb_utils.TESTING: + # Restart adb to work around bugs, sleep to wait for usb discovery. + device_utils.RestartServer() + RunCmd(['sleep', '1']) + provision_cmd = ['build/android/provision_devices.py', '-t', options.target] + if options.auto_reconnect: + provision_cmd.append('--auto-reconnect') + if options.skip_wipe: + provision_cmd.append('--skip-wipe') + if options.disable_location: + provision_cmd.append('--disable-location') + RunCmd(provision_cmd, halt_on_failure=True) + + +def DeviceStatusCheck(options): + bb_annotations.PrintNamedStep('device_status_check') + cmd = ['build/android/buildbot/bb_device_status_check.py'] + if options.restart_usb: + cmd.append('--restart-usb') + RunCmd(cmd, halt_on_failure=True) + + +def GetDeviceSetupStepCmds(): + return [ + ('device_status_check', DeviceStatusCheck), + ('provision_devices', ProvisionDevices), + ] + + +def RunUnitTests(options): + suites = gtest_config.STABLE_TEST_SUITES + if options.asan: + suites = [s for s in suites + if s not in gtest_config.ASAN_EXCLUDED_TEST_SUITES] + RunTestSuites(options, suites) + + +def RunTelemetryUnitTests(options): + RunTelemetryTests(options, 'telemetry_unittests', 'tools/telemetry/run_tests') + + +def RunTelemetryPerfUnitTests(options): + RunTelemetryTests(options, 'telemetry_perf_unittests', 'tools/perf/run_tests') + + +def RunInstrumentationTests(options): + for test in INSTRUMENTATION_TESTS.itervalues(): + RunInstrumentationSuite(options, test) + + +def RunWebkitTests(options): + RunTestSuites(options, ['webkit_unit_tests', 'blink_heap_unittests']) + RunWebkitLint() + + +def RunGPUTests(options): + revision = _GetRevision(options) + builder_name = options.build_properties.get('buildername', 'noname') + + bb_annotations.PrintNamedStep('pixel_tests') + RunCmd(['content/test/gpu/run_gpu_test.py', + 'pixel', '-v', + '--browser', + 'android-content-shell', + '--build-revision', + str(revision), + '--upload-refimg-to-cloud-storage', + '--refimg-cloud-storage-bucket', + 'chromium-gpu-archive/reference-images', + '--os-type', + 'android', + '--test-machine-name', + EscapeBuilderName(builder_name)]) + + bb_annotations.PrintNamedStep('webgl_conformance_tests') + RunCmd(['content/test/gpu/run_gpu_test.py', '-v', + '--browser=android-content-shell', 'webgl_conformance', + '--webgl-conformance-version=1.0.1']) + + bb_annotations.PrintNamedStep('android_webview_webgl_conformance_tests') + RunCmd(['content/test/gpu/run_gpu_test.py', '-v', + '--browser=android-webview-shell', 'webgl_conformance', + '--webgl-conformance-version=1.0.1']) + + bb_annotations.PrintNamedStep('gpu_rasterization_tests') + RunCmd(['content/test/gpu/run_gpu_test.py', + 'gpu_rasterization', '-v', + '--browser', + 'android-content-shell', + '--build-revision', + str(revision), + '--test-machine-name', + EscapeBuilderName(builder_name)]) + + +def RunPythonUnitTests(_options): + for suite in constants.PYTHON_UNIT_TEST_SUITES: + bb_annotations.PrintNamedStep(suite) + RunCmd(['build/android/test_runner.py', 'python', '-s', suite]) + + +def GetTestStepCmds(): + return [ + ('base_junit_tests', + lambda _options: RunJunitSuite('base_junit_tests')), + ('chromedriver', RunChromeDriverTests), + ('chrome_proxy', RunChromeProxyTests), + ('components_browsertests', + lambda options: RunTestSuites(options, ['components_browsertests'])), + ('gfx_unittests', + lambda options: RunTestSuites(options, ['gfx_unittests'])), + ('gl_unittests', + lambda options: RunTestSuites(options, ['gl_unittests'])), + ('gpu', RunGPUTests), + ('python_unittests', RunPythonUnitTests), + ('telemetry_unittests', RunTelemetryUnitTests), + ('telemetry_perf_unittests', RunTelemetryPerfUnitTests), + ('ui', RunInstrumentationTests), + ('unit', RunUnitTests), + ('webkit', RunWebkitTests), + ('webkit_layout', RunWebkitLayoutTests), + ] + + +def MakeGSPath(options, gs_base_dir): + revision = _GetRevision(options) + bot_id = options.build_properties.get('buildername', 'testing') + randhash = hashlib.sha1(str(random.random())).hexdigest() + gs_path = '%s/%s/%s/%s' % (gs_base_dir, bot_id, revision, randhash) + # remove double slashes, happens with blank revisions and confuses gsutil + gs_path = re.sub('/+', '/', gs_path) + return gs_path + +def UploadHTML(options, gs_base_dir, dir_to_upload, link_text, + link_rel_path='index.html', gs_url=GS_URL): + """Uploads directory at |dir_to_upload| to Google Storage and output a link. + + Args: + options: Command line options. + gs_base_dir: The Google Storage base directory (e.g. + 'chromium-code-coverage/java') + dir_to_upload: Absolute path to the directory to be uploaded. + link_text: Link text to be displayed on the step. + link_rel_path: Link path relative to |dir_to_upload|. + gs_url: Google storage URL. + """ + gs_path = MakeGSPath(options, gs_base_dir) + RunCmd([bb_utils.GSUTIL_PATH, 'cp', '-R', dir_to_upload, 'gs://%s' % gs_path]) + bb_annotations.PrintLink(link_text, + '%s/%s/%s' % (gs_url, gs_path, link_rel_path)) + + +def GenerateJavaCoverageReport(options): + """Generates an HTML coverage report using EMMA and uploads it.""" + bb_annotations.PrintNamedStep('java_coverage_report') + + coverage_html = os.path.join(options.coverage_dir, 'coverage_html') + RunCmd(['build/android/generate_emma_html.py', + '--coverage-dir', options.coverage_dir, + '--metadata-dir', os.path.join(CHROME_OUT_DIR, options.target), + '--cleanup', + '--output', os.path.join(coverage_html, 'index.html')]) + return coverage_html + + +def LogcatDump(options): + # Print logcat, kill logcat monitor + bb_annotations.PrintNamedStep('logcat_dump') + logcat_file = os.path.join(CHROME_OUT_DIR, options.target, 'full_log.txt') + RunCmd([SrcPath('build', 'android', 'adb_logcat_printer.py'), + '--output-path', logcat_file, LOGCAT_DIR]) + gs_path = MakeGSPath(options, 'chromium-android/logcat_dumps') + RunCmd([bb_utils.GSUTIL_PATH, 'cp', '-z', 'txt', logcat_file, + 'gs://%s' % gs_path]) + bb_annotations.PrintLink('logcat dump', '%s/%s' % (GS_AUTH_URL, gs_path)) + + +def RunStackToolSteps(options): + """Run stack tool steps. + + Stack tool is run for logcat dump, optionally for ASAN. + """ + bb_annotations.PrintNamedStep('Run stack tool with logcat dump') + logcat_file = os.path.join(CHROME_OUT_DIR, options.target, 'full_log.txt') + RunCmd([os.path.join(CHROME_SRC_DIR, 'third_party', 'android_platform', + 'development', 'scripts', 'stack'), + '--more-info', logcat_file]) + if options.asan_symbolize: + bb_annotations.PrintNamedStep('Run stack tool for ASAN') + RunCmd([ + os.path.join(CHROME_SRC_DIR, 'build', 'android', 'asan_symbolize.py'), + '-l', logcat_file]) + + +def GenerateTestReport(options): + bb_annotations.PrintNamedStep('test_report') + for report in glob.glob( + os.path.join(CHROME_OUT_DIR, options.target, 'test_logs', '*.log')): + RunCmd(['cat', report]) + os.remove(report) + + +def MainTestWrapper(options): + try: + # Spawn logcat monitor + SpawnLogcatMonitor() + + # Run all device setup steps + for _, cmd in GetDeviceSetupStepCmds(): + cmd(options) + + if options.install: + for i in options.install: + install_obj = INSTALLABLE_PACKAGES[i] + InstallApk(options, install_obj, print_step=True) + + if options.test_filter: + bb_utils.RunSteps(options.test_filter, GetTestStepCmds(), options) + + if options.coverage_bucket: + coverage_html = GenerateJavaCoverageReport(options) + UploadHTML(options, '%s/java' % options.coverage_bucket, coverage_html, + 'Coverage Report') + shutil.rmtree(coverage_html, ignore_errors=True) + + if options.experimental: + RunTestSuites(options, gtest_config.EXPERIMENTAL_TEST_SUITES) + + finally: + # Run all post test steps + LogcatDump(options) + if not options.disable_stack_tool: + RunStackToolSteps(options) + GenerateTestReport(options) + # KillHostHeartbeat() has logic to check if heartbeat process is running, + # and kills only if it finds the process is running on the host. + provision_devices.KillHostHeartbeat() + if options.cleanup: + shutil.rmtree(os.path.join(CHROME_OUT_DIR, options.target), + ignore_errors=True) + + +def GetDeviceStepsOptParser(): + parser = bb_utils.GetParser() + parser.add_option('--experimental', action='store_true', + help='Run experiemental tests') + parser.add_option('-f', '--test-filter', metavar='', default=[], + action='append', + help=('Run a test suite. Test suites: "%s"' % + '", "'.join(VALID_TESTS))) + parser.add_option('--gtest-filter', + help='Filter for running a subset of tests of a gtest test') + parser.add_option('--asan', action='store_true', help='Run tests with asan.') + parser.add_option('--install', metavar='', action="append", + help='Install an apk by name') + parser.add_option('--no-reboot', action='store_true', + help='Do not reboot devices during provisioning.') + parser.add_option('--coverage-bucket', + help=('Bucket name to store coverage results. Coverage is ' + 'only run if this is set.')) + parser.add_option('--restart-usb', action='store_true', + help='Restart usb ports before device status check.') + parser.add_option( + '--flakiness-server', + help=('The flakiness dashboard server to which the results should be ' + 'uploaded.')) + parser.add_option( + '--auto-reconnect', action='store_true', + help='Push script to device which restarts adbd on disconnections.') + parser.add_option('--skip-wipe', action='store_true', + help='Do not wipe devices during provisioning.') + parser.add_option('--disable-location', action='store_true', + help='Disable location settings.') + parser.add_option( + '--logcat-dump-output', + help='The logcat dump output will be "tee"-ed into this file') + # During processing perf bisects, a seperate working directory created under + # which builds are produced. Therefore we should look for relevent output + # file under this directory.(/b/build/slave//build/bisect/src/out) + parser.add_option( + '--chrome-output-dir', + help='Chrome output directory to be used while bisecting.') + + parser.add_option('--disable-stack-tool', action='store_true', + help='Do not run stack tool.') + parser.add_option('--asan-symbolize', action='store_true', + help='Run stack tool for ASAN') + parser.add_option('--cleanup', action='store_true', + help='Delete out/ directory at the end of the run.') + return parser + + +def main(argv): + parser = GetDeviceStepsOptParser() + options, args = parser.parse_args(argv[1:]) + + if args: + return sys.exit('Unused args %s' % args) + + unknown_tests = set(options.test_filter) - VALID_TESTS + if unknown_tests: + return sys.exit('Unknown tests %s' % list(unknown_tests)) + + setattr(options, 'target', options.factory_properties.get('target', 'Debug')) + + if options.chrome_output_dir: + global CHROME_OUT_DIR + global LOGCAT_DIR + CHROME_OUT_DIR = options.chrome_output_dir + LOGCAT_DIR = os.path.join(CHROME_OUT_DIR, 'logcat') + + if options.coverage_bucket: + setattr(options, 'coverage_dir', + os.path.join(CHROME_OUT_DIR, options.target, 'coverage')) + + MainTestWrapper(options) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/buildbot/bb_host_steps.py b/engine/src/flutter/build/android/buildbot/bb_host_steps.py new file mode 100755 index 0000000000..1e927fb399 --- /dev/null +++ b/engine/src/flutter/build/android/buildbot/bb_host_steps.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# Copyright 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. + +import os +import json +import sys + +import bb_utils +import bb_annotations + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from pylib import constants + + +SLAVE_SCRIPTS_DIR = os.path.join(bb_utils.BB_BUILD_DIR, 'scripts', 'slave') +VALID_HOST_TESTS = set(['check_webview_licenses']) + +DIR_BUILD_ROOT = os.path.dirname(constants.DIR_SOURCE_ROOT) + +# Short hand for RunCmd which is used extensively in this file. +RunCmd = bb_utils.RunCmd + + +def SrcPath(*path): + return os.path.join(constants.DIR_SOURCE_ROOT, *path) + + +def CheckWebViewLicenses(_): + bb_annotations.PrintNamedStep('check_licenses') + RunCmd([SrcPath('android_webview', 'tools', 'webview_licenses.py'), 'scan'], + warning_code=1) + + +def RunHooks(build_type): + RunCmd([SrcPath('build', 'landmines.py')]) + build_path = SrcPath('out', build_type) + landmine_path = os.path.join(build_path, '.landmines_triggered') + clobber_env = os.environ.get('BUILDBOT_CLOBBER') + if clobber_env or os.path.isfile(landmine_path): + bb_annotations.PrintNamedStep('Clobber') + if not clobber_env: + print 'Clobbering due to triggered landmines:' + with open(landmine_path) as f: + print f.read() + RunCmd(['rm', '-rf', build_path]) + + bb_annotations.PrintNamedStep('runhooks') + RunCmd(['gclient', 'runhooks'], halt_on_failure=True) + + +def Compile(options): + RunHooks(options.target) + cmd = [os.path.join(SLAVE_SCRIPTS_DIR, 'compile.py'), + '--build-tool=ninja', + '--compiler=goma', + '--target=%s' % options.target, + '--goma-dir=%s' % bb_utils.GOMA_DIR] + bb_annotations.PrintNamedStep('compile') + if options.build_targets: + build_targets = options.build_targets.split(',') + cmd += ['--build-args', ' '.join(build_targets)] + RunCmd(cmd, halt_on_failure=True, cwd=DIR_BUILD_ROOT) + + +def ZipBuild(options): + bb_annotations.PrintNamedStep('zip_build') + RunCmd([ + os.path.join(SLAVE_SCRIPTS_DIR, 'zip_build.py'), + '--src-dir', constants.DIR_SOURCE_ROOT, + '--exclude-files', 'lib.target,gen,android_webview,jingle_unittests'] + + bb_utils.EncodeProperties(options), cwd=DIR_BUILD_ROOT) + + +def ExtractBuild(options): + bb_annotations.PrintNamedStep('extract_build') + RunCmd([os.path.join(SLAVE_SCRIPTS_DIR, 'extract_build.py')] + + bb_utils.EncodeProperties(options), cwd=DIR_BUILD_ROOT) + + +def BisectPerfRegression(options): + args = [] + if options.extra_src: + args = ['--extra_src', options.extra_src] + RunCmd([SrcPath('tools', 'prepare-bisect-perf-regression.py'), + '-w', os.path.join(constants.DIR_SOURCE_ROOT, os.pardir)]) + RunCmd([SrcPath('tools', 'run-bisect-perf-regression.py'), + '-w', os.path.join(constants.DIR_SOURCE_ROOT, os.pardir), + '--build-properties=%s' % json.dumps(options.build_properties)] + + args) + + +def GetHostStepCmds(): + return [ + ('compile', Compile), + ('extract_build', ExtractBuild), + ('check_webview_licenses', CheckWebViewLicenses), + ('bisect_perf_regression', BisectPerfRegression), + ('zip_build', ZipBuild) + ] + + +def GetHostStepsOptParser(): + parser = bb_utils.GetParser() + parser.add_option('--steps', help='Comma separated list of host tests.') + parser.add_option('--build-targets', default='', + help='Comma separated list of build targets.') + parser.add_option('--experimental', action='store_true', + help='Indicate whether to compile experimental targets.') + parser.add_option('--extra_src', default='', + help='Path to extra source file. If this is supplied, ' + 'bisect script will use it to override default behavior.') + + return parser + + +def main(argv): + parser = GetHostStepsOptParser() + options, args = parser.parse_args(argv[1:]) + if args: + return sys.exit('Unused args %s' % args) + + setattr(options, 'target', options.factory_properties.get('target', 'Debug')) + setattr(options, 'extra_src', + options.factory_properties.get('extra_src', '')) + + if options.steps: + bb_utils.RunSteps(options.steps.split(','), GetHostStepCmds(), options) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/buildbot/bb_run_bot.py b/engine/src/flutter/build/android/buildbot/bb_run_bot.py new file mode 100755 index 0000000000..0c8a977c9d --- /dev/null +++ b/engine/src/flutter/build/android/buildbot/bb_run_bot.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python +# +# 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. + +import collections +import copy +import json +import os +import pipes +import re +import subprocess +import sys + +import bb_utils + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from pylib import constants + + +CHROMIUM_COVERAGE_BUCKET = 'chromium-code-coverage' + +_BotConfig = collections.namedtuple( + 'BotConfig', ['bot_id', 'host_obj', 'test_obj']) + +HostConfig = collections.namedtuple( + 'HostConfig', + ['script', 'host_steps', 'extra_args', 'extra_gyp_defines', 'target_arch']) + +TestConfig = collections.namedtuple('Tests', ['script', 'tests', 'extra_args']) + + +def BotConfig(bot_id, host_object, test_object=None): + return _BotConfig(bot_id, host_object, test_object) + + +def DictDiff(d1, d2): + diff = [] + for key in sorted(set(d1.keys() + d2.keys())): + if key in d1 and d1[key] != d2.get(key): + diff.append('- %s=%s' % (key, pipes.quote(d1[key]))) + if key in d2 and d2[key] != d1.get(key): + diff.append('+ %s=%s' % (key, pipes.quote(d2[key]))) + return '\n'.join(diff) + + +def GetEnvironment(host_obj, testing, extra_env_vars=None): + init_env = dict(os.environ) + init_env['GYP_GENERATORS'] = 'ninja' + if extra_env_vars: + init_env.update(extra_env_vars) + envsetup_cmd = '. build/android/envsetup.sh' + if testing: + # Skip envsetup to avoid presubmit dependence on android deps. + print 'Testing mode - skipping "%s"' % envsetup_cmd + envsetup_cmd = ':' + else: + print 'Running %s' % envsetup_cmd + proc = subprocess.Popen(['bash', '-exc', + envsetup_cmd + ' >&2; python build/android/buildbot/env_to_json.py'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + cwd=bb_utils.CHROME_SRC, env=init_env) + json_env, envsetup_output = proc.communicate() + if proc.returncode != 0: + print >> sys.stderr, 'FATAL Failure in envsetup.' + print >> sys.stderr, envsetup_output + sys.exit(1) + env = json.loads(json_env) + env['GYP_DEFINES'] = env.get('GYP_DEFINES', '') + \ + ' OS=android fastbuild=1 use_goma=1 gomadir=%s' % bb_utils.GOMA_DIR + if host_obj.target_arch: + env['GYP_DEFINES'] += ' target_arch=%s' % host_obj.target_arch + extra_gyp = host_obj.extra_gyp_defines + if extra_gyp: + env['GYP_DEFINES'] += ' %s' % extra_gyp + if re.search('(asan|clang)=1', extra_gyp): + env.pop('CXX_target', None) + + # Bots checkout chrome in /b/build/slave//build/src + build_internal_android = os.path.abspath(os.path.join( + bb_utils.CHROME_SRC, '..', '..', '..', '..', '..', 'build_internal', + 'scripts', 'slave', 'android')) + if os.path.exists(build_internal_android): + env['PATH'] = os.pathsep.join([build_internal_android, env['PATH']]) + return env + + +def GetCommands(options, bot_config): + """Get a formatted list of commands. + + Args: + options: Options object. + bot_config: A BotConfig named tuple. + host_step_script: Host step script. + device_step_script: Device step script. + Returns: + list of Command objects. + """ + property_args = bb_utils.EncodeProperties(options) + commands = [[bot_config.host_obj.script, + '--steps=%s' % ','.join(bot_config.host_obj.host_steps)] + + property_args + (bot_config.host_obj.extra_args or [])] + + test_obj = bot_config.test_obj + if test_obj: + run_test_cmd = [test_obj.script] + property_args + for test in test_obj.tests: + run_test_cmd.extend(['-f', test]) + if test_obj.extra_args: + run_test_cmd.extend(test_obj.extra_args) + commands.append(run_test_cmd) + return commands + + +def GetBotStepMap(): + compile_step = ['compile'] + chrome_proxy_tests = ['chrome_proxy'] + python_unittests = ['python_unittests'] + std_host_tests = ['check_webview_licenses'] + std_build_steps = ['compile', 'zip_build'] + std_test_steps = ['extract_build'] + std_tests = ['ui', 'unit'] + telemetry_tests = ['telemetry_perf_unittests'] + telemetry_tests_user_build = ['telemetry_unittests', + 'telemetry_perf_unittests'] + trial_tests = [ + 'base_junit_tests', + 'components_browsertests', + 'gfx_unittests', + 'gl_unittests', + ] + flakiness_server = ( + '--flakiness-server=%s' % constants.UPSTREAM_FLAKINESS_SERVER) + experimental = ['--experimental'] + bisect_chrome_output_dir = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, + os.pardir, 'bisect', 'src', 'out')) + B = BotConfig + H = (lambda steps, extra_args=None, extra_gyp=None, target_arch=None: + HostConfig('build/android/buildbot/bb_host_steps.py', steps, extra_args, + extra_gyp, target_arch)) + T = (lambda tests, extra_args=None: + TestConfig('build/android/buildbot/bb_device_steps.py', tests, + extra_args)) + + bot_configs = [ + # Main builders + B('main-builder-dbg', H(std_build_steps + std_host_tests)), + B('main-builder-rel', H(std_build_steps)), + B('main-clang-builder', + H(compile_step, extra_gyp='clang=1 component=shared_library')), + B('main-clobber', H(compile_step)), + B('main-tests-rel', H(std_test_steps), + T(std_tests + telemetry_tests + chrome_proxy_tests, + ['--cleanup', flakiness_server])), + B('main-tests', H(std_test_steps), + T(std_tests, ['--cleanup', flakiness_server])), + + # Other waterfalls + B('asan-builder-tests', H(compile_step, + extra_gyp='asan=1 component=shared_library'), + T(std_tests, ['--asan', '--asan-symbolize'])), + B('blink-try-builder', H(compile_step)), + B('chromedriver-fyi-tests-dbg', H(std_test_steps), + T(['chromedriver'], + ['--install=ChromeShell', '--install=ChromeDriverWebViewShell', + '--skip-wipe', '--disable-location', '--cleanup'])), + B('fyi-x86-builder-dbg', + H(compile_step + std_host_tests, experimental, target_arch='ia32')), + B('fyi-builder-dbg', + H(std_build_steps + std_host_tests, experimental, + extra_gyp='emma_coverage=1')), + B('x86-builder-dbg', + H(compile_step + std_host_tests, target_arch='ia32')), + B('fyi-builder-rel', H(std_build_steps, experimental)), + B('fyi-tests', H(std_test_steps), + T(std_tests + python_unittests, + ['--experimental', flakiness_server, + '--coverage-bucket', CHROMIUM_COVERAGE_BUCKET, + '--cleanup'])), + B('user-build-fyi-tests-dbg', H(std_test_steps), + T(sorted(telemetry_tests_user_build + trial_tests))), + B('fyi-component-builder-tests-dbg', + H(compile_step, extra_gyp='component=shared_library'), + T(std_tests, ['--experimental', flakiness_server])), + B('gpu-builder-tests-dbg', + H(compile_step), + T(['gpu'], ['--install=ContentShell'])), + # Pass empty T([]) so that logcat monitor and device status check are run. + B('perf-bisect-builder-tests-dbg', + H(['bisect_perf_regression']), + T([], ['--chrome-output-dir', bisect_chrome_output_dir])), + B('perf-tests-rel', H(std_test_steps), + T([], ['--install=ChromeShell', '--cleanup'])), + B('webkit-latest-webkit-tests', H(std_test_steps), + T(['webkit_layout', 'webkit'], ['--cleanup', '--auto-reconnect'])), + B('webkit-latest-contentshell', H(compile_step), + T(['webkit_layout'], ['--auto-reconnect'])), + B('builder-unit-tests', H(compile_step), T(['unit'])), + + # Generic builder config (for substring match). + B('builder', H(std_build_steps)), + ] + + bot_map = dict((config.bot_id, config) for config in bot_configs) + + # These bots have identical configuration to ones defined earlier. + copy_map = [ + ('lkgr-clobber', 'main-clobber'), + ('try-builder-dbg', 'main-builder-dbg'), + ('try-builder-rel', 'main-builder-rel'), + ('try-clang-builder', 'main-clang-builder'), + ('try-fyi-builder-dbg', 'fyi-builder-dbg'), + ('try-x86-builder-dbg', 'x86-builder-dbg'), + ('try-tests-rel', 'main-tests-rel'), + ('try-tests', 'main-tests'), + ('try-fyi-tests', 'fyi-tests'), + ('webkit-latest-tests', 'main-tests'), + ] + for to_id, from_id in copy_map: + assert to_id not in bot_map + # pylint: disable=W0212 + bot_map[to_id] = copy.deepcopy(bot_map[from_id])._replace(bot_id=to_id) + + # Trybots do not upload to flakiness dashboard. They should be otherwise + # identical in configuration to their trunk building counterparts. + test_obj = bot_map[to_id].test_obj + if to_id.startswith('try') and test_obj: + extra_args = test_obj.extra_args + if extra_args and flakiness_server in extra_args: + extra_args.remove(flakiness_server) + return bot_map + + +# Return an object from the map, looking first for an exact id match. +# If this fails, look for an id which is a substring of the specified id. +# Choose the longest of all substring matches. +# pylint: disable=W0622 +def GetBestMatch(id_map, id): + config = id_map.get(id) + if not config: + substring_matches = [x for x in id_map.iterkeys() if x in id] + if substring_matches: + max_id = max(substring_matches, key=len) + print 'Using config from id="%s" (substring match).' % max_id + config = id_map[max_id] + return config + + +def GetRunBotOptParser(): + parser = bb_utils.GetParser() + parser.add_option('--bot-id', help='Specify bot id directly.') + parser.add_option('--testing', action='store_true', + help='For testing: print, but do not run commands') + + return parser + + +def GetBotConfig(options, bot_step_map): + bot_id = options.bot_id or options.factory_properties.get('android_bot_id') + if not bot_id: + print (sys.stderr, + 'A bot id must be specified through option or factory_props.') + return + + bot_config = GetBestMatch(bot_step_map, bot_id) + if not bot_config: + print 'Error: config for id="%s" cannot be inferred.' % bot_id + return bot_config + + +def RunBotCommands(options, commands, env): + print 'Environment changes:' + print DictDiff(dict(os.environ), env) + + for command in commands: + print bb_utils.CommandToString(command) + sys.stdout.flush() + if options.testing: + env['BUILDBOT_TESTING'] = '1' + return_code = subprocess.call(command, cwd=bb_utils.CHROME_SRC, env=env) + if return_code != 0: + return return_code + + +def main(argv): + proc = subprocess.Popen( + ['/bin/hostname', '-f'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + hostname_stdout, hostname_stderr = proc.communicate() + if proc.returncode == 0: + print 'Running on: ' + hostname_stdout + else: + print >> sys.stderr, 'WARNING: failed to run hostname' + print >> sys.stderr, hostname_stdout + print >> sys.stderr, hostname_stderr + sys.exit(1) + + parser = GetRunBotOptParser() + options, args = parser.parse_args(argv[1:]) + if args: + parser.error('Unused args: %s' % args) + + bot_config = GetBotConfig(options, GetBotStepMap()) + if not bot_config: + sys.exit(1) + + print 'Using config:', bot_config + + commands = GetCommands(options, bot_config) + for command in commands: + print 'Will run: ', bb_utils.CommandToString(command) + print + + env = GetEnvironment(bot_config.host_obj, options.testing) + return RunBotCommands(options, commands, env) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/buildbot/bb_utils.py b/engine/src/flutter/build/android/buildbot/bb_utils.py new file mode 100644 index 0000000000..3c16cc2bc5 --- /dev/null +++ b/engine/src/flutter/build/android/buildbot/bb_utils.py @@ -0,0 +1,100 @@ +# Copyright 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. + +import json +import optparse +import os +import pipes +import subprocess +import sys + +import bb_annotations + +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +from pylib import constants + + +TESTING = 'BUILDBOT_TESTING' in os.environ + +BB_BUILD_DIR = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, + os.pardir, os.pardir, os.pardir, os.pardir)) + +CHROME_SRC = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..', '..')) + +# TODO: Figure out how to merge this with pylib.cmd_helper.OutDirectory(). +CHROME_OUT_DIR = os.path.join(CHROME_SRC, 'out') + +GOMA_DIR = os.environ.get('GOMA_DIR', os.path.join(BB_BUILD_DIR, 'goma')) + +GSUTIL_PATH = os.path.join(BB_BUILD_DIR, 'third_party', 'gsutil', 'gsutil') + +def CommandToString(command): + """Returns quoted command that can be run in bash shell.""" + return ' '.join(map(pipes.quote, command)) + + +def SpawnCmd(command, stdout=None, cwd=CHROME_SRC): + """Spawn a process without waiting for termination.""" + print '>', CommandToString(command) + sys.stdout.flush() + if TESTING: + class MockPopen(object): + @staticmethod + def wait(): + return 0 + @staticmethod + def communicate(): + return '', '' + return MockPopen() + return subprocess.Popen(command, cwd=cwd, stdout=stdout) + + +def RunCmd(command, flunk_on_failure=True, halt_on_failure=False, + warning_code=constants.WARNING_EXIT_CODE, stdout=None, + cwd=CHROME_SRC): + """Run a command relative to the chrome source root.""" + code = SpawnCmd(command, stdout, cwd).wait() + print '<', CommandToString(command) + if code != 0: + print 'ERROR: process exited with code %d' % code + if code != warning_code and flunk_on_failure: + bb_annotations.PrintError() + else: + bb_annotations.PrintWarning() + # Allow steps to have both halting (i.e. 1) and non-halting exit codes. + if code != warning_code and halt_on_failure: + print 'FATAL %d != %d' % (code, warning_code) + sys.exit(1) + return code + + +def GetParser(): + def ConvertJson(option, _, value, parser): + setattr(parser.values, option.dest, json.loads(value)) + parser = optparse.OptionParser() + parser.add_option('--build-properties', action='callback', + callback=ConvertJson, type='string', default={}, + help='build properties in JSON format') + parser.add_option('--factory-properties', action='callback', + callback=ConvertJson, type='string', default={}, + help='factory properties in JSON format') + return parser + + +def EncodeProperties(options): + return ['--factory-properties=%s' % json.dumps(options.factory_properties), + '--build-properties=%s' % json.dumps(options.build_properties)] + + +def RunSteps(steps, step_cmds, options): + unknown_steps = set(steps) - set(step for step, _ in step_cmds) + if unknown_steps: + print >> sys.stderr, 'FATAL: Unknown steps %s' % list(unknown_steps) + sys.exit(1) + + for step, cmd in step_cmds: + if step in steps: + cmd(options) diff --git a/engine/src/flutter/build/android/buildbot/env_to_json.py b/engine/src/flutter/build/android/buildbot/env_to_json.py new file mode 100755 index 0000000000..f9a7a443d3 --- /dev/null +++ b/engine/src/flutter/build/android/buildbot/env_to_json.py @@ -0,0 +1,11 @@ +#!/usr/bin/python +# Copyright 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. + +# Encode current environment into json. + +import json +import os + +print json.dumps(dict(os.environ)) diff --git a/engine/src/flutter/build/android/buildbot/tests/bb_run_bot_test.py b/engine/src/flutter/build/android/buildbot/tests/bb_run_bot_test.py new file mode 100755 index 0000000000..810c60dac2 --- /dev/null +++ b/engine/src/flutter/build/android/buildbot/tests/bb_run_bot_test.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# 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. + +import os +import subprocess +import sys + +BUILDBOT_DIR = os.path.join(os.path.dirname(__file__), '..') +sys.path.append(BUILDBOT_DIR) +import bb_run_bot + +def RunBotProcesses(bot_process_map): + code = 0 + for bot, proc in bot_process_map: + _, err = proc.communicate() + code |= proc.returncode + if proc.returncode != 0: + print 'Error running the bot script with id="%s"' % bot, err + + return code + + +def main(): + procs = [ + (bot, subprocess.Popen( + [os.path.join(BUILDBOT_DIR, 'bb_run_bot.py'), '--bot-id', bot, + '--testing'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)) + for bot in bb_run_bot.GetBotStepMap()] + return RunBotProcesses(procs) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/chrome_with_libs.gyp b/engine/src/flutter/build/android/chrome_with_libs.gyp new file mode 100644 index 0000000000..690be885f0 --- /dev/null +++ b/engine/src/flutter/build/android/chrome_with_libs.gyp @@ -0,0 +1,82 @@ +# Copyright 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. + +# This file is meant to add more loadable libs into Chrome_apk. +# +# This is useful when building Chrome_apk with some loadable modules which are +# not included in Chrome_apk. +# As an example, when building Chrome_apk with +# libpeer_target_type=loadable_module, +# the libpeerconnection.so is not included in Chrome_apk. To add the missing +# lib, follow the steps below: +# - Run gyp: +# GYP_DEFINES="$GYP_DEFINES libpeer_target_type=loadable_module" CHROMIUM_GYP_FILE="build/android/chrome_with_libs.gyp" build/gyp_chromium +# - Build chrome_with_libs: +# ninja (or make) chrome_with_libs +# +# This tool also allows replacing the loadable module with a new one via the +# following steps: +# - Build Chrome_apk with the gyp define: +# GYP_DEFINES="$GYP_DEFINES libpeer_target_type=loadable_module" build/gyp_chromium +# ninja (or make) Chrome_apk +# - Replace libpeerconnection.so with a new one: +# cp the_new_one path/to/libpeerconnection.so +# - Run gyp: +# GYP_DEFINES="$GYP_DEFINES libpeer_target_type=loadable_module" CHROMIUM_GYP_FILE="build/android/chrome_with_libs.gyp" build/gyp_chromium +# - Build chrome_with_libs: +# ninja (or make) chrome_with_libs +{ + 'targets': [ + { + # An "All" target is required for a top-level gyp-file. + 'target_name': 'All', + 'type': 'none', + 'dependencies': [ + 'chrome_with_libs', + ], + }, + { + 'target_name': 'chrome_with_libs', + 'type': 'none', + 'variables': { + 'intermediate_dir': '<(PRODUCT_DIR)/prebuilt_libs/', + 'chrome_unsigned_path': '<(PRODUCT_DIR)/chrome_apk/Chrome-unsigned.apk', + 'chrome_with_libs_unsigned': '<(intermediate_dir)/Chrome-with-libs-unsigned.apk', + 'chrome_with_libs_final': '<(PRODUCT_DIR)/apks/Chrome-with-libs.apk', + }, + 'dependencies': [ + '<(DEPTH)/clank/native/framework/clank.gyp:chrome_apk' + ], + 'copies': [ + { + 'destination': '<(intermediate_dir)/lib/<(android_app_abi)', + 'files': [ + '<(PRODUCT_DIR)/libpeerconnection.so', + ], + }, + ], + 'actions': [ + { + 'action_name': 'put_libs_in_chrome', + 'variables': { + 'inputs': [ + '<(intermediate_dir)/lib/<(android_app_abi)/libpeerconnection.so', + ], + 'input_apk_path': '<(chrome_unsigned_path)', + 'output_apk_path': '<(chrome_with_libs_unsigned)', + 'libraries_top_dir%': '<(intermediate_dir)', + }, + 'includes': [ 'create_standalone_apk_action.gypi' ], + }, + { + 'action_name': 'finalize_chrome_with_libs', + 'variables': { + 'input_apk_path': '<(chrome_with_libs_unsigned)', + 'output_apk_path': '<(chrome_with_libs_final)', + }, + 'includes': [ 'finalize_apk_action.gypi'], + }, + ], + }], +} diff --git a/engine/src/flutter/build/android/empty/src/.keep b/engine/src/flutter/build/android/empty/src/.keep new file mode 100644 index 0000000000..0f710b673d --- /dev/null +++ b/engine/src/flutter/build/android/empty/src/.keep @@ -0,0 +1,6 @@ +This is a file that needs to live here until http://crbug.com/158155 has +been fixed. + +The ant build system requires that a src folder is always present, and for +some of our targets that is not the case. Giving it an empty src-folder works +nicely though. diff --git a/engine/src/flutter/build/android/empty_proguard.flags b/engine/src/flutter/build/android/empty_proguard.flags new file mode 100644 index 0000000000..53484fe815 --- /dev/null +++ b/engine/src/flutter/build/android/empty_proguard.flags @@ -0,0 +1 @@ +# Used for apk targets that do not need proguard. See build/java_apk.gypi. diff --git a/engine/src/flutter/build/android/enable_asserts.py b/engine/src/flutter/build/android/enable_asserts.py new file mode 100755 index 0000000000..8fb7dca472 --- /dev/null +++ b/engine/src/flutter/build/android/enable_asserts.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 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. + +"""Enables dalvik vm asserts in the android device.""" + +import argparse +import sys + +from pylib.device import device_utils + + +def main(): + parser = argparse.ArgumentParser() + + set_asserts_group = parser.add_mutually_exclusive_group(required=True) + set_asserts_group.add_argument( + '--enable_asserts', dest='set_asserts', action='store_true', + help='Sets the dalvik.vm.enableassertions property to "all"') + set_asserts_group.add_argument( + '--disable_asserts', dest='set_asserts', action='store_false', + help='Removes the dalvik.vm.enableassertions property') + + args = parser.parse_args() + + # TODO(jbudorick): Accept optional serial number and run only for the + # specified device when present. + devices = device_utils.DeviceUtils.parallel() + + def set_java_asserts_and_restart(device): + if device.SetJavaAsserts(args.set_asserts): + device.RunShellCommand('stop') + device.RunShellCommand('start') + + devices.pMap(set_java_asserts_and_restart) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/envsetup.sh b/engine/src/flutter/build/android/envsetup.sh new file mode 100755 index 0000000000..0545330bb2 --- /dev/null +++ b/engine/src/flutter/build/android/envsetup.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Copyright (c) 2012 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. + +# Sets up environment for building Chromium on Android. + +# Make sure we're being sourced (possibly by another script). Check for bash +# since zsh sets $0 when sourcing. +if [[ -n "$BASH_VERSION" && "${BASH_SOURCE:-$0}" == "$0" ]]; then + echo "ERROR: envsetup must be sourced." + exit 1 +fi + +# This only exists to set local variables. Don't call this manually. +android_envsetup_main() { + local SCRIPT_PATH="$1" + local SCRIPT_DIR="$(dirname "$SCRIPT_PATH")" + + local CURRENT_DIR="$(readlink -f "${SCRIPT_DIR}/../../")" + if [[ -z "${CHROME_SRC}" ]]; then + # If $CHROME_SRC was not set, assume current directory is CHROME_SRC. + local CHROME_SRC="${CURRENT_DIR}" + fi + + if [[ "${CURRENT_DIR/"${CHROME_SRC}"/}" == "${CURRENT_DIR}" ]]; then + # If current directory is not in $CHROME_SRC, it might be set for other + # source tree. If $CHROME_SRC was set correctly and we are in the correct + # directory, "${CURRENT_DIR/"${CHROME_SRC}"/}" will be "". + # Otherwise, it will equal to "${CURRENT_DIR}" + echo "Warning: Current directory is out of CHROME_SRC, it may not be \ + the one you want." + echo "${CHROME_SRC}" + fi + + # Allow the caller to override a few environment variables. If any of them is + # unset, we default to a sane value that's known to work. This allows for + # experimentation with a custom SDK. + if [[ -z "${ANDROID_SDK_ROOT}" || ! -d "${ANDROID_SDK_ROOT}" ]]; then + local ANDROID_SDK_ROOT="${CHROME_SRC}/third_party/android_tools/sdk/" + fi + + # Add Android SDK tools to system path. + export PATH=$PATH:${ANDROID_SDK_ROOT}/platform-tools + + # Add Android utility tools to the system path. + export PATH=$PATH:${ANDROID_SDK_ROOT}/tools/ + + # Add Chromium Android development scripts to system path. + # Must be after CHROME_SRC is set. + export PATH=$PATH:${CHROME_SRC}/build/android + + export ENVSETUP_GYP_CHROME_SRC=${CHROME_SRC} # TODO(thakis): Remove. +} +# In zsh, $0 is the name of the file being sourced. +android_envsetup_main "${BASH_SOURCE:-$0}" +unset -f android_envsetup_main + +android_gyp() { + echo "Please call build/gyp_chromium instead. android_gyp is going away." + "${ENVSETUP_GYP_CHROME_SRC}/build/gyp_chromium" --depth="${ENVSETUP_GYP_CHROME_SRC}" --check "$@" +} diff --git a/engine/src/flutter/build/android/findbugs_diff.py b/engine/src/flutter/build/android/findbugs_diff.py new file mode 100755 index 0000000000..f55e46261b --- /dev/null +++ b/engine/src/flutter/build/android/findbugs_diff.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 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. + +"""Runs findbugs, and returns an error code if there are new warnings. + +Other options + --only-analyze used to only analyze the class you are interested. + --relase-build analyze the classes in out/Release directory. + --findbugs-args used to passin other findbugs's options. + +Run + $CHROMIUM_SRC/third_party/findbugs/bin/findbugs -textui for details. + +""" + +import argparse +import os +import sys + +from pylib import constants +from pylib.utils import findbugs + +_DEFAULT_BASE_DIR = os.path.join( + constants.DIR_SOURCE_ROOT, 'build', 'android', 'findbugs_filter') + +sys.path.append( + os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'android', 'gyp')) +from util import build_utils + + +def main(): + parser = argparse.ArgumentParser() + + parser.add_argument( + '-a', '--auxclasspath', default=None, dest='auxclasspath', + help='Set aux classpath for analysis.') + parser.add_argument( + '--auxclasspath-gyp', dest='auxclasspath_gyp', + help='A gyp list containing the aux classpath for analysis') + parser.add_argument( + '-o', '--only-analyze', default=None, + dest='only_analyze', help='Only analyze the given classes and packages.') + parser.add_argument( + '-e', '--exclude', default=None, dest='exclude', + help='Exclude bugs matching given filter.') + parser.add_argument( + '-l', '--release-build', action='store_true', dest='release_build', + help='Analyze release build instead of debug.') + parser.add_argument( + '-f', '--findbug-args', default=None, dest='findbug_args', + help='Additional findbug arguments.') + parser.add_argument( + '-b', '--base-dir', default=_DEFAULT_BASE_DIR, + dest='base_dir', help='Base directory for configuration file.') + parser.add_argument( + '--output-file', dest='output_file', + help='Path to save the output to.') + parser.add_argument( + '--stamp', help='Path to touch on success.') + parser.add_argument( + '--depfile', help='Path to the depfile. This must be specified as the ' + "action's first output.") + + parser.add_argument( + 'jar_paths', metavar='JAR_PATH', nargs='+', + help='JAR file to analyze') + + args = parser.parse_args(build_utils.ExpandFileArgs(sys.argv[1:])) + if args.auxclasspath: + args.auxclasspath = args.auxclasspath.split(':') + elif args.auxclasspath_gyp: + args.auxclasspath = build_utils.ParseGypList(args.auxclasspath_gyp) + + if args.base_dir: + if not args.exclude: + args.exclude = os.path.join(args.base_dir, 'findbugs_exclude.xml') + + findbugs_command, findbugs_warnings = findbugs.Run( + args.exclude, args.only_analyze, args.auxclasspath, + args.output_file, args.findbug_args, args.jar_paths) + + if findbugs_warnings: + print + print '*' * 80 + print 'FindBugs run via:' + print findbugs_command + print + print 'FindBugs reported the following issues:' + for warning in sorted(findbugs_warnings): + print str(warning) + print '*' * 80 + print + else: + if args.depfile: + build_utils.WriteDepfile( + args.depfile, + build_utils.GetPythonDependencies() + args.auxclasspath + + args.jar_paths) + if args.stamp: + build_utils.Touch(args.stamp) + + return len(findbugs_warnings) + + +if __name__ == '__main__': + sys.exit(main()) + diff --git a/engine/src/flutter/build/android/findbugs_filter/findbugs_exclude.xml b/engine/src/flutter/build/android/findbugs_filter/findbugs_exclude.xml new file mode 100644 index 0000000000..dbff9d9667 --- /dev/null +++ b/engine/src/flutter/build/android/findbugs_filter/findbugs_exclude.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/engine/src/flutter/build/android/generate_emma_html.py b/engine/src/flutter/build/android/generate_emma_html.py new file mode 100755 index 0000000000..93b0b0e59c --- /dev/null +++ b/engine/src/flutter/build/android/generate_emma_html.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +# Copyright 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. + +"""Aggregates EMMA coverage files to produce html output.""" + +import fnmatch +import json +import optparse +import os +import sys + +from pylib import cmd_helper +from pylib import constants + + +def _GetFilesWithExt(root_dir, ext): + """Gets all files with a given extension. + + Args: + root_dir: Directory in which to search for files. + ext: Extension to look for (including dot) + + Returns: + A list of absolute paths to files that match. + """ + files = [] + for root, _, filenames in os.walk(root_dir): + basenames = fnmatch.filter(filenames, '*.' + ext) + files.extend([os.path.join(root, basename) + for basename in basenames]) + + return files + + +def main(): + option_parser = optparse.OptionParser() + option_parser.add_option('--output', help='HTML output filename.') + option_parser.add_option('--coverage-dir', default=None, + help=('Root of the directory in which to search for ' + 'coverage data (.ec) files.')) + option_parser.add_option('--metadata-dir', default=None, + help=('Root of the directory in which to search for ' + 'coverage metadata (.em) files.')) + option_parser.add_option('--cleanup', action='store_true', + help=('If set, removes coverage files generated at ' + 'runtime.')) + options, _ = option_parser.parse_args() + + if not (options.coverage_dir and options.metadata_dir and options.output): + option_parser.error('One or more mandatory options are missing.') + + coverage_files = _GetFilesWithExt(options.coverage_dir, 'ec') + metadata_files = _GetFilesWithExt(options.metadata_dir, 'em') + print 'Found coverage files: %s' % str(coverage_files) + print 'Found metadata files: %s' % str(metadata_files) + + sources = [] + for f in metadata_files: + sources_file = os.path.splitext(f)[0] + '_sources.txt' + with open(sources_file, 'r') as sf: + sources.extend(json.load(sf)) + sources = [os.path.join(constants.DIR_SOURCE_ROOT, s) for s in sources] + print 'Sources: %s' % sources + + input_args = [] + for f in coverage_files + metadata_files: + input_args.append('-in') + input_args.append(f) + + output_args = ['-Dreport.html.out.file', options.output] + source_args = ['-sp', ','.join(sources)] + + exit_code = cmd_helper.RunCmd( + ['java', '-cp', + os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'lib', 'emma.jar'), + 'emma', 'report', '-r', 'html'] + + input_args + output_args + source_args) + + if options.cleanup: + for f in coverage_files: + os.remove(f) + + return exit_code + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gn/zip.py b/engine/src/flutter/build/android/gn/zip.py new file mode 100755 index 0000000000..5050ea0710 --- /dev/null +++ b/engine/src/flutter/build/android/gn/zip.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Archives a set of files. +""" + +import ast +import optparse +import os +import sys +import zipfile + +sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'gyp')) +from util import build_utils + +def DoZip(inputs, output, base_dir): + with zipfile.ZipFile(output, 'w') as outfile: + for f in inputs: + outfile.write(f, os.path.relpath(f, base_dir)) + +def main(): + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option('--inputs', help='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 = ast.literal_eval(options.inputs) + output = options.output + base_dir = options.base_dir + + DoZip(inputs, output, base_dir) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + build_utils.GetPythonDependencies()) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gyp/aidl.py b/engine/src/flutter/build/android/gyp/aidl.py new file mode 100755 index 0000000000..d5aa546770 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/aidl.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Invokes Android's aidl +""" + +import optparse +import os +import sys + +from util import build_utils + + +def main(argv): + option_parser = optparse.OptionParser() + build_utils.AddDepfileOption(option_parser) + option_parser.add_option('--aidl-path', help='Path to the aidl binary.') + option_parser.add_option('--imports', help='Files to import.') + option_parser.add_option('--includes', + help='Directories to add as import search paths.') + option_parser.add_option('--srcjar', help='Path for srcjar output.') + options, args = option_parser.parse_args(argv[1:]) + + with build_utils.TempDir() as temp_dir: + for f in args: + classname = os.path.splitext(os.path.basename(f))[0] + output = os.path.join(temp_dir, classname + '.java') + aidl_cmd = [options.aidl_path] + aidl_cmd += [ + '-p' + s for s in build_utils.ParseGypList(options.imports) + ] + if options.includes is not None: + aidl_cmd += [ + '-I' + s for s in build_utils.ParseGypList(options.includes) + ] + aidl_cmd += [ + f, + output + ] + build_utils.CheckOutput(aidl_cmd) + + build_utils.ZipDir(options.srcjar, temp_dir) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + build_utils.GetPythonDependencies()) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/gyp/ant.py b/engine/src/flutter/build/android/gyp/ant.py new file mode 100755 index 0000000000..5394b9ec7d --- /dev/null +++ b/engine/src/flutter/build/android/gyp/ant.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# Copyright 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. + +"""An Ant wrapper that suppresses useless Ant output. + +Ant build scripts output "BUILD SUCCESSFUL" and build timing at the end of +every build. In the Android build, this just adds a lot of useless noise to the +build output. This script forwards its arguments to ant, and prints Ant's +output up until the BUILD SUCCESSFUL line. + +Also, when a command fails, this script will re-run that ant command with the +'-verbose' argument so that the failure is easier to debug. +""" + +import optparse +import sys +import traceback + +from util import build_utils + + +def main(argv): + option_parser = optparse.OptionParser() + build_utils.AddDepfileOption(option_parser) + options, args = option_parser.parse_args(argv[1:]) + + try: + stdout = build_utils.CheckOutput(['ant'] + args) + except build_utils.CalledProcessError: + # It is very difficult to diagnose ant failures without the '-verbose' + # argument. So, when an ant command fails, re-run it with '-verbose' so that + # the cause of the failure is easier to identify. + verbose_args = ['-verbose'] + [a for a in args if a != '-quiet'] + try: + stdout = build_utils.CheckOutput(['ant'] + verbose_args) + except build_utils.CalledProcessError: + traceback.print_exc() + sys.exit(1) + + # If this did sys.exit(1), building again would succeed (which would be + # awkward). Instead, just print a big warning. + build_utils.PrintBigWarning( + 'This is unexpected. `ant ' + ' '.join(args) + '` failed.' + + 'But, running `ant ' + ' '.join(verbose_args) + '` passed.') + + stdout = stdout.strip().split('\n') + for line in stdout: + if line.strip() == 'BUILD SUCCESSFUL': + break + print line + + if options.depfile: + assert '-buildfile' in args + ant_buildfile = args[args.index('-buildfile') + 1] + + build_utils.WriteDepfile( + options.depfile, + [ant_buildfile] + build_utils.GetPythonDependencies()) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/gyp/apk_install.py b/engine/src/flutter/build/android/gyp/apk_install.py new file mode 100755 index 0000000000..c9098676f1 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/apk_install.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# +# Copyright 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. + +"""Installs an APK. + +""" + +import optparse +import os +import re +import sys + +from util import build_device +from util import build_utils +from util import md5_check + +BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..') +sys.path.append(BUILD_ANDROID_DIR) + +from pylib import constants +from pylib.utils import apk_helper + +_DPI_TO_DENSITY = { + 120: 'ldpi', + 160: 'mdpi', + 240: 'hdpi', + 320: 'xhdpi', + 480: 'xxhdpi', + } + + +def RetrieveDeviceConfig(device): + """Probes the given device for its split-select config. + + For example: en-rUS-xhdpi:armeabi-v7a + Run "split-select --help" for more info about the format. + """ + language = device.GetProp('persist.sys.language') + country = device.GetProp('persist.sys.country') + density_dpi = int(device.GetProp('ro.sf.lcd_density')) + density = _DPI_TO_DENSITY.get(density_dpi, 'tvdpi') + abi = device.product_cpu_abi + return '%s-r%s-%s:%s' % (language, country, density, abi) + + +def GetNewMetadata(device, apk_package): + """Gets the metadata on the device for the apk_package apk.""" + output = device.RunShellCommand('ls -l /data/app/') + # Matches lines like: + # -rw-r--r-- system system 7376582 2013-04-19 16:34 \ + # org.chromium.chrome.shell.apk + # -rw-r--r-- system system 7376582 2013-04-19 16:34 \ + # org.chromium.chrome.shell-1.apk + apk_matcher = lambda s: re.match('.*%s(-[0-9]*)?(.apk)?$' % apk_package, s) + matches = filter(apk_matcher, output) + return matches[0] if matches else None + +def HasInstallMetadataChanged(device, apk_package, metadata_path): + """Checks if the metadata on the device for apk_package has changed.""" + if not os.path.exists(metadata_path): + return True + + with open(metadata_path, 'r') as expected_file: + return expected_file.read() != device.GetInstallMetadata(apk_package) + + +def RecordInstallMetadata(device, apk_package, metadata_path): + """Records the metadata from the device for apk_package.""" + metadata = GetNewMetadata(device, apk_package) + if not metadata: + raise Exception('APK install failed unexpectedly.') + + with open(metadata_path, 'w') as outfile: + outfile.write(metadata) + + +def main(): + parser = optparse.OptionParser() + parser.add_option('--apk-path', + help='Path to .apk to install.') + parser.add_option('--split-apk-path', + help='Path to .apk splits (can specify multiple times, causes ' + '--install-multiple to be used.', + action='append') + parser.add_option('--android-sdk-tools', + help='Path to the Android SDK build tools folder. ' + + 'Required when using --split-apk-path.') + parser.add_option('--install-record', + help='Path to install record (touched only when APK is installed).') + parser.add_option('--build-device-configuration', + help='Path to build device configuration.') + parser.add_option('--stamp', + help='Path to touch on success.') + parser.add_option('--configuration-name', + help='The build CONFIGURATION_NAME') + options, _ = parser.parse_args() + + device = build_device.GetBuildDeviceFromPath( + options.build_device_configuration) + if not device: + return + + constants.SetBuildType(options.configuration_name) + + serial_number = device.GetSerialNumber() + apk_package = apk_helper.GetPackageName(options.apk_path) + + metadata_path = '%s.%s.device.time.stamp' % (options.apk_path, serial_number) + + # If the APK on the device does not match the one that was last installed by + # the build, then the APK has to be installed (regardless of the md5 record). + force_install = HasInstallMetadataChanged(device, apk_package, metadata_path) + + def SelectSplits(target_config, base_apk, split_apks, android_sdk_tools): + cmd = [os.path.join(android_sdk_tools, 'split-select'), + '--target', target_config, + '--base', base_apk, + ] + for split in split_apks: + cmd.extend(('--split', split)) + + # split-select outputs one path per line and a blank line at the end. + output = build_utils.CheckOutput(cmd) + return [x for x in output.split('\n') if x] + + def Install(): + if options.split_apk_path: + requiredSdkVersion = constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP + actualSdkVersion = device.device.build_version_sdk + if actualSdkVersion < requiredSdkVersion: + raise Exception(('--split-apk-path requires sdk version %s. Device has ' + 'version %s') % (requiredSdkVersion, actualSdkVersion)) + device_config = RetrieveDeviceConfig(device.device) + active_splits = SelectSplits( + device_config, + options.apk_path, + options.split_apk_path, + options.android_sdk_tools) + + all_apks = [options.apk_path] + active_splits + device.device.adb.InstallMultiple(all_apks, reinstall=True) + else: + device.Install(options.apk_path, reinstall=True) + + RecordInstallMetadata(device, apk_package, metadata_path) + build_utils.Touch(options.install_record) + + + record_path = '%s.%s.md5.stamp' % (options.apk_path, serial_number) + md5_check.CallAndRecordIfStale( + Install, + record_path=record_path, + input_paths=[options.apk_path], + force=force_install) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gyp/apk_obfuscate.py b/engine/src/flutter/build/android/gyp/apk_obfuscate.py new file mode 100755 index 0000000000..b075758800 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/apk_obfuscate.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Generates the obfuscated jar and test jar for an apk. + +If proguard is not enabled or 'Release' is not in the configuration name, +obfuscation will be a no-op. +""" + +import optparse +import os +import sys + +from util import build_utils +from util import proguard_util + + +def ParseArgs(argv): + parser = optparse.OptionParser() + parser.add_option('--android-sdk', help='path to the Android SDK folder') + parser.add_option('--android-sdk-tools', + help='path to the Android SDK build tools folder') + parser.add_option('--android-sdk-jar', + help='path to Android SDK\'s android.jar') + parser.add_option('--proguard-jar-path', + help='Path to proguard.jar in the sdk') + parser.add_option('--input-jars-paths', + help='Path to jars to include in obfuscated jar') + + parser.add_option('--proguard-configs', + help='Paths to proguard config files') + + parser.add_option('--configuration-name', + help='Gyp configuration name (i.e. Debug, Release)') + parser.add_option('--proguard-enabled', action='store_true', + help='Set if proguard is enabled for this target.') + + parser.add_option('--obfuscated-jar-path', + help='Output path for obfuscated jar.') + + parser.add_option('--testapp', action='store_true', + help='Set this if building an instrumentation test apk') + parser.add_option('--tested-apk-obfuscated-jar-path', + help='Path to obfusctated jar of the tested apk') + parser.add_option('--test-jar-path', + help='Output path for jar containing all the test apk\'s ' + 'code.') + + parser.add_option('--stamp', help='File to touch on success') + + (options, args) = parser.parse_args(argv) + + if args: + parser.error('No positional arguments should be given. ' + str(args)) + + # Check that required options have been provided. + required_options = ( + 'android_sdk', + 'android_sdk_tools', + 'android_sdk_jar', + 'proguard_jar_path', + 'input_jars_paths', + 'configuration_name', + 'obfuscated_jar_path', + ) + + if options.testapp: + required_options += ( + 'test_jar_path', + ) + + build_utils.CheckOptions(options, parser, required=required_options) + return options, args + + +def DoProguard(options): + proguard = proguard_util.ProguardCmdBuilder(options.proguard_jar_path) + proguard.outjar(options.obfuscated_jar_path) + + library_classpath = [options.android_sdk_jar] + input_jars = build_utils.ParseGypList(options.input_jars_paths) + + exclude_paths = [] + configs = build_utils.ParseGypList(options.proguard_configs) + if options.tested_apk_obfuscated_jar_path: + # configs should only contain the process_resources.py generated config. + assert len(configs) == 1, ( + 'test apks should not have custom proguard configs: ' + str(configs)) + tested_jar_info = build_utils.ReadJson( + options.tested_apk_obfuscated_jar_path + '.info') + exclude_paths = tested_jar_info['inputs'] + configs = tested_jar_info['configs'] + + proguard.is_test(True) + proguard.mapping(options.tested_apk_obfuscated_jar_path + '.mapping') + library_classpath.append(options.tested_apk_obfuscated_jar_path) + + proguard.libraryjars(library_classpath) + proguard_injars = [p for p in input_jars if p not in exclude_paths] + proguard.injars(proguard_injars) + proguard.configs(configs) + + proguard.CheckOutput() + + this_info = { + 'inputs': proguard_injars, + 'configs': configs + } + + build_utils.WriteJson( + this_info, options.obfuscated_jar_path + '.info') + + +def main(argv): + options, _ = ParseArgs(argv) + + input_jars = build_utils.ParseGypList(options.input_jars_paths) + + if options.testapp: + dependency_class_filters = [ + '*R.class', '*R$*.class', '*Manifest.class', '*BuildConfig.class'] + build_utils.MergeZips( + options.test_jar_path, input_jars, dependency_class_filters) + + if options.configuration_name == 'Release' and options.proguard_enabled: + DoProguard(options) + else: + output_files = [ + options.obfuscated_jar_path, + options.obfuscated_jar_path + '.info', + options.obfuscated_jar_path + '.dump', + options.obfuscated_jar_path + '.seeds', + options.obfuscated_jar_path + '.usage', + options.obfuscated_jar_path + '.mapping'] + for f in output_files: + if os.path.exists(f): + os.remove(f) + build_utils.Touch(f) + + if options.stamp: + build_utils.Touch(options.stamp) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/copy_ex.py b/engine/src/flutter/build/android/gyp/copy_ex.py new file mode 100755 index 0000000000..a474e77065 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/copy_ex.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Copies files to a directory.""" + +import optparse +import os +import shutil +import sys + +from util import build_utils + + +def _get_all_files(base): + """Returns a list of all the files in |base|. Each entry is relative to the + last path entry of |base|.""" + result = [] + dirname = os.path.dirname(base) + for root, _, files in os.walk(base): + result.extend([os.path.join(root[len(dirname):], f) for f in files]) + return result + + +def main(args): + args = build_utils.ExpandFileArgs(args) + + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option('--dest', help='Directory to copy files to.') + parser.add_option('--files', action='append', + help='List of files to copy.') + parser.add_option('--clear', action='store_true', + help='If set, the destination directory will be deleted ' + 'before copying files to it. This is highly recommended to ' + 'ensure that no stale files are left in the directory.') + parser.add_option('--stamp', help='Path to touch on success.') + + options, _ = parser.parse_args(args) + + if options.clear: + build_utils.DeleteDirectory(options.dest) + build_utils.MakeDirectory(options.dest) + + files = [] + for file_arg in options.files: + files += build_utils.ParseGypList(file_arg) + + deps = [] + + for f in files: + if os.path.isdir(f): + if not options.clear: + print ('To avoid stale files you must use --clear when copying ' + 'directories') + sys.exit(-1) + shutil.copytree(f, os.path.join(options.dest, os.path.basename(f))) + deps.extend(_get_all_files(f)) + else: + shutil.copy(f, options.dest) + deps.append(f) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + deps + build_utils.GetPythonDependencies()) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) + diff --git a/engine/src/flutter/build/android/gyp/create_device_library_links.py b/engine/src/flutter/build/android/gyp/create_device_library_links.py new file mode 100755 index 0000000000..3e630b67c9 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/create_device_library_links.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# +# Copyright 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. + +"""Creates symlinks to native libraries for an APK. + +The native libraries should have previously been pushed to the device (in +options.target_dir). This script then creates links in an apk's lib/ folder to +those native libraries. +""" + +import optparse +import os +import sys + +from util import build_device +from util import build_utils + +BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..') +sys.path.append(BUILD_ANDROID_DIR) + +from pylib import constants +from pylib.utils import apk_helper + +def RunShellCommand(device, cmd): + output = device.RunShellCommand(cmd) + + if output: + raise Exception( + 'Unexpected output running command: ' + cmd + '\n' + + '\n'.join(output)) + + +def CreateSymlinkScript(options): + libraries = build_utils.ParseGypList(options.libraries) + + link_cmd = ( + 'rm $APK_LIBRARIES_DIR/%(lib_basename)s > /dev/null 2>&1 \n' + 'ln -s $STRIPPED_LIBRARIES_DIR/%(lib_basename)s ' + '$APK_LIBRARIES_DIR/%(lib_basename)s \n' + ) + + script = '#!/bin/sh \n' + + for lib in libraries: + script += link_cmd % { 'lib_basename': lib } + + with open(options.script_host_path, 'w') as scriptfile: + scriptfile.write(script) + + +def TriggerSymlinkScript(options): + device = build_device.GetBuildDeviceFromPath( + options.build_device_configuration) + if not device: + return + + apk_package = apk_helper.GetPackageName(options.apk) + apk_libraries_dir = '/data/data/%s/lib' % apk_package + + device_dir = os.path.dirname(options.script_device_path) + mkdir_cmd = ('if [ ! -e %(dir)s ]; then mkdir -p %(dir)s; fi ' % + { 'dir': device_dir }) + RunShellCommand(device, mkdir_cmd) + device.PushChangedFiles([(options.script_host_path, + options.script_device_path)]) + + trigger_cmd = ( + 'APK_LIBRARIES_DIR=%(apk_libraries_dir)s; ' + 'STRIPPED_LIBRARIES_DIR=%(target_dir)s; ' + '. %(script_device_path)s' + ) % { + 'apk_libraries_dir': apk_libraries_dir, + 'target_dir': options.target_dir, + 'script_device_path': options.script_device_path + } + RunShellCommand(device, trigger_cmd) + + +def main(args): + args = build_utils.ExpandFileArgs(args) + parser = optparse.OptionParser() + parser.add_option('--apk', help='Path to the apk.') + parser.add_option('--script-host-path', + help='Path on the host for the symlink script.') + parser.add_option('--script-device-path', + help='Path on the device to push the created symlink script.') + parser.add_option('--libraries', + help='List of native libraries.') + parser.add_option('--target-dir', + help='Device directory that contains the target libraries for symlinks.') + parser.add_option('--stamp', help='Path to touch on success.') + parser.add_option('--build-device-configuration', + help='Path to build device configuration.') + parser.add_option('--configuration-name', + help='The build CONFIGURATION_NAME') + options, _ = parser.parse_args(args) + + required_options = ['apk', 'libraries', 'script_host_path', + 'script_device_path', 'target_dir', 'configuration_name'] + build_utils.CheckOptions(options, parser, required=required_options) + constants.SetBuildType(options.configuration_name) + + CreateSymlinkScript(options) + TriggerSymlinkScript(options) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/create_dist_jar.py b/engine/src/flutter/build/android/gyp/create_dist_jar.py new file mode 100755 index 0000000000..0d31c5db93 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/create_dist_jar.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Merges a list of jars into a single jar.""" + +import optparse +import sys + +from util import build_utils + +def main(args): + args = build_utils.ExpandFileArgs(args) + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option('--output', help='Path to output jar.') + parser.add_option('--inputs', action='append', help='List of jar inputs.') + options, _ = parser.parse_args(args) + build_utils.CheckOptions(options, parser, ['output', 'inputs']) + + input_jars = [] + for inputs_arg in options.inputs: + input_jars.extend(build_utils.ParseGypList(inputs_arg)) + + build_utils.MergeZips(options.output, input_jars) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + input_jars + build_utils.GetPythonDependencies()) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/create_java_binary_script.py b/engine/src/flutter/build/android/gyp/create_java_binary_script.py new file mode 100755 index 0000000000..d632728ac7 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/create_java_binary_script.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Creates a simple script to run a java "binary". + +This creates a script that sets up the java command line for running a java +jar. This includes correctly setting the classpath and the main class. +""" + +import optparse +import os +import sys + +from util import build_utils + +# The java command must be executed in the current directory because there may +# be user-supplied paths in the args. The script receives the classpath relative +# to the directory that the script is written in and then, when run, must +# recalculate the paths relative to the current directory. +script_template = """\ +#!/usr/bin/env python +# +# This file was generated by build/android/gyp/create_java_binary_script.py + +import os +import sys + +self_dir = os.path.dirname(__file__) +classpath = [{classpath}] +if os.getcwd() != self_dir: + offset = os.path.relpath(self_dir, os.getcwd()) + classpath = [os.path.join(offset, p) for p in classpath] +java_args = [ + "java", + "-classpath", ":".join(classpath), + \"{main_class}\"] + sys.argv[1:] +os.execvp("java", java_args) +""" + +def main(argv): + argv = build_utils.ExpandFileArgs(argv) + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option('--output', help='Output path for executable script.') + parser.add_option('--jar-path', help='Path to the main jar.') + parser.add_option('--main-class', + help='Name of the java class with the "main" entry point.') + parser.add_option('--classpath', action='append', + help='Classpath for running the jar.') + options, _ = parser.parse_args(argv) + + classpath = [options.jar_path] + for cp_arg in options.classpath: + classpath += build_utils.ParseGypList(cp_arg) + + run_dir = os.path.dirname(options.output) + classpath = [os.path.relpath(p, run_dir) for p in classpath] + + with open(options.output, 'w') as script: + script.write(script_template.format( + classpath=('"%s"' % '", "'.join(classpath)), + main_class=options.main_class)) + + os.chmod(options.output, 0750) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + build_utils.GetPythonDependencies()) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/create_placeholder_files.py b/engine/src/flutter/build/android/gyp/create_placeholder_files.py new file mode 100755 index 0000000000..103e1df7f2 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/create_placeholder_files.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +"""Create placeholder files. +""" + +import optparse +import os +import sys + +from util import build_utils + +def main(): + parser = optparse.OptionParser() + parser.add_option( + '--dest-lib-dir', + help='Destination directory to have placeholder files.') + parser.add_option( + '--stamp', + help='Path to touch on success') + + options, args = parser.parse_args() + + for name in args: + target_path = os.path.join(options.dest_lib_dir, name) + build_utils.Touch(target_path) + + if options.stamp: + build_utils.Touch(options.stamp) + +if __name__ == '__main__': + sys.exit(main()) + diff --git a/engine/src/flutter/build/android/gyp/create_standalone_apk.py b/engine/src/flutter/build/android/gyp/create_standalone_apk.py new file mode 100755 index 0000000000..c560599286 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/create_standalone_apk.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# Copyright 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. + +"""Combines stripped libraries and incomplete APK into single standalone APK. + +""" + +import optparse +import os +import shutil +import sys +import tempfile + +from util import build_utils +from util import md5_check + +def CreateStandaloneApk(options): + def DoZip(): + with tempfile.NamedTemporaryFile(suffix='.zip') as intermediate_file: + intermediate_path = intermediate_file.name + shutil.copy(options.input_apk_path, intermediate_path) + apk_path_abs = os.path.abspath(intermediate_path) + build_utils.CheckOutput( + ['zip', '-r', '-1', apk_path_abs, 'lib'], + cwd=options.libraries_top_dir) + shutil.copy(intermediate_path, options.output_apk_path) + + input_paths = [options.input_apk_path, options.libraries_top_dir] + record_path = '%s.standalone.stamp' % options.input_apk_path + md5_check.CallAndRecordIfStale( + DoZip, + record_path=record_path, + input_paths=input_paths) + + +def main(): + parser = optparse.OptionParser() + parser.add_option('--libraries-top-dir', + help='Top directory that contains libraries ' + '(i.e. library paths are like ' + 'libraries_top_dir/lib/android_app_abi/foo.so).') + parser.add_option('--input-apk-path', help='Path to incomplete APK.') + parser.add_option('--output-apk-path', help='Path for standalone APK.') + parser.add_option('--stamp', help='Path to touch on success.') + options, _ = parser.parse_args() + + required_options = ['libraries_top_dir', 'input_apk_path', 'output_apk_path'] + build_utils.CheckOptions(options, parser, required=required_options) + + CreateStandaloneApk(options) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gyp/create_test_runner_script.py b/engine/src/flutter/build/android/gyp/create_test_runner_script.py new file mode 100755 index 0000000000..e72f364aad --- /dev/null +++ b/engine/src/flutter/build/android/gyp/create_test_runner_script.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# +# Copyright 2015 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. + +"""Creates a script to run an android test using build/android/test_runner.py. +""" + +import argparse +import os +import sys + +from util import build_utils + +SCRIPT_TEMPLATE = """\ +#!/usr/bin/env python +# +# This file was generated by build/android/gyp/create_test_runner_script.py + +import logging +import os +import sys + +def main(): + script_directory = os.path.dirname(__file__) + + def ResolvePath(path): + \"\"\"Returns an absolute filepath given a path relative to this script. + \"\"\" + return os.path.abspath(os.path.join(script_directory, path)) + + test_runner_path = ResolvePath('{test_runner_path}') + test_runner_args = {test_runner_args} + test_runner_path_args = {test_runner_path_args} + for arg, path in test_runner_path_args.iteritems(): + test_runner_args.extend([arg, ResolvePath(path)]) + + test_runner_cmd = ' '.join( + [test_runner_path] + test_runner_args + sys.argv[1:]) + logging.critical(test_runner_cmd) + os.system(test_runner_cmd) + +if __name__ == '__main__': + sys.exit(main()) +""" + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--script-output-path', + help='Output path for executable script.') + parser.add_argument('--depfile', + help='Path to the depfile. This must be specified as ' + "the action's first output.") + # We need to intercept any test runner path arguments and make all + # of the paths relative to the output script directory. + group = parser.add_argument_group('Test runner path arguments.') + group.add_argument('--output-directory') + group.add_argument('--isolate-file-path') + args, test_runner_args = parser.parse_known_args() + + def RelativizePathToScript(path): + """Returns the path relative to the output script directory.""" + return os.path.relpath(path, os.path.dirname(args.script_output_path)) + + test_runner_path = os.path.join( + os.path.dirname(__file__), os.path.pardir, 'test_runner.py') + test_runner_path = RelativizePathToScript(test_runner_path) + + test_runner_path_args = {} + if args.output_directory: + test_runner_path_args['--output-directory'] = RelativizePathToScript( + args.output_directory) + if args.isolate_file_path: + test_runner_path_args['--isolate-file-path'] = RelativizePathToScript( + args.isolate_file_path) + + with open(args.script_output_path, 'w') as script: + script.write(SCRIPT_TEMPLATE.format( + test_runner_path=str(test_runner_path), + test_runner_args=str(test_runner_args), + test_runner_path_args=str(test_runner_path_args))) + + os.chmod(args.script_output_path, 0750) + + if args.depfile: + build_utils.WriteDepfile( + args.depfile, + build_utils.GetPythonDependencies()) + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file diff --git a/engine/src/flutter/build/android/gyp/dex.py b/engine/src/flutter/build/android/gyp/dex.py new file mode 100755 index 0000000000..4e6233270a --- /dev/null +++ b/engine/src/flutter/build/android/gyp/dex.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# +# Copyright 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. + +import optparse +import os +import sys + +from util import build_utils +from util import md5_check + + +def DoDex(options, paths): + dx_binary = os.path.join(options.android_sdk_tools, 'dx') + # See http://crbug.com/272064 for context on --force-jumbo. + dex_cmd = [dx_binary, '--dex', '--force-jumbo', '--output', options.dex_path] + if options.no_locals != '0': + dex_cmd.append('--no-locals') + + dex_cmd += paths + + record_path = '%s.md5.stamp' % options.dex_path + md5_check.CallAndRecordIfStale( + lambda: build_utils.CheckOutput(dex_cmd, print_stderr=False), + record_path=record_path, + input_paths=paths, + input_strings=dex_cmd, + force=not os.path.exists(options.dex_path)) + build_utils.WriteJson(paths, options.dex_path + '.inputs') + + +def main(): + args = build_utils.ExpandFileArgs(sys.argv[1:]) + + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option('--android-sdk-tools', + help='Android sdk build tools directory.') + parser.add_option('--dex-path', help='Dex output path.') + parser.add_option('--configuration-name', + help='The build CONFIGURATION_NAME.') + parser.add_option('--proguard-enabled', + help='"true" if proguard is enabled.') + parser.add_option('--proguard-enabled-input-path', + help=('Path to dex in Release mode when proguard ' + 'is enabled.')) + parser.add_option('--no-locals', + help='Exclude locals list from the dex file.') + parser.add_option('--inputs', help='A list of additional input paths.') + parser.add_option('--excluded-paths', + help='A list of paths to exclude from the dex file.') + + options, paths = parser.parse_args(args) + + required_options = ('android_sdk_tools',) + build_utils.CheckOptions(options, parser, required=required_options) + + if (options.proguard_enabled == 'true' + and options.configuration_name == 'Release'): + paths = [options.proguard_enabled_input_path] + + if options.inputs: + paths += build_utils.ParseGypList(options.inputs) + + if options.excluded_paths: + exclude_paths = build_utils.ParseGypList(options.excluded_paths) + paths = [p for p in paths if not p in exclude_paths] + + DoDex(options, paths) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + paths + build_utils.GetPythonDependencies()) + + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gyp/emma_instr.py b/engine/src/flutter/build/android/gyp/emma_instr.py new file mode 100755 index 0000000000..6f3555a327 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/emma_instr.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# +# Copyright 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. + +"""Instruments classes and jar files. + +This script corresponds to the 'emma_instr' action in the java build process. +Depending on whether emma_instrument is set, the 'emma_instr' action will either +call one of the instrument commands, or the copy command. + +Possible commands are: +- instrument_jar: Accepts a jar and instruments it using emma.jar. +- instrument_classes: Accepts a directory containing java classes and + instruments it using emma.jar. +- copy: Called when EMMA coverage is not enabled. This allows us to make + this a required step without necessarily instrumenting on every build. + Also removes any stale coverage files. +""" + +import collections +import json +import os +import shutil +import sys +import tempfile + +sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) +from pylib.utils import command_option_parser + +from util import build_utils + + +def _AddCommonOptions(option_parser): + """Adds common options to |option_parser|.""" + option_parser.add_option('--input-path', + help=('Path to input file(s). Either the classes ' + 'directory, or the path to a jar.')) + option_parser.add_option('--output-path', + help=('Path to output final file(s) to. Either the ' + 'final classes directory, or the directory in ' + 'which to place the instrumented/copied jar.')) + option_parser.add_option('--stamp', help='Path to touch when done.') + option_parser.add_option('--coverage-file', + help='File to create with coverage metadata.') + option_parser.add_option('--sources-file', + help='File to create with the list of sources.') + + +def _AddInstrumentOptions(option_parser): + """Adds options related to instrumentation to |option_parser|.""" + _AddCommonOptions(option_parser) + option_parser.add_option('--sources', + help='Space separated list of sources.') + option_parser.add_option('--src-root', + help='Root of the src repository.') + option_parser.add_option('--emma-jar', + help='Path to emma.jar.') + option_parser.add_option( + '--filter-string', default='', + help=('Filter string consisting of a list of inclusion/exclusion ' + 'patterns separated with whitespace and/or comma.')) + + +def _RunCopyCommand(_command, options, _, option_parser): + """Copies the jar from input to output locations. + + Also removes any old coverage/sources file. + + Args: + command: String indicating the command that was received to trigger + this function. + options: optparse options dictionary. + args: List of extra args from optparse. + option_parser: optparse.OptionParser object. + + Returns: + An exit code. + """ + if not (options.input_path and options.output_path and + options.coverage_file and options.sources_file): + option_parser.error('All arguments are required.') + + coverage_file = os.path.join(os.path.dirname(options.output_path), + options.coverage_file) + sources_file = os.path.join(os.path.dirname(options.output_path), + options.sources_file) + if os.path.exists(coverage_file): + os.remove(coverage_file) + if os.path.exists(sources_file): + os.remove(sources_file) + + if os.path.isdir(options.input_path): + shutil.rmtree(options.output_path, ignore_errors=True) + shutil.copytree(options.input_path, options.output_path) + else: + shutil.copy(options.input_path, options.output_path) + + if options.stamp: + build_utils.Touch(options.stamp) + + +def _CreateSourcesFile(sources_string, sources_file, src_root): + """Adds all normalized source directories to |sources_file|. + + Args: + sources_string: String generated from gyp containing the list of sources. + sources_file: File into which to write the JSON list of sources. + src_root: Root which sources added to the file should be relative to. + + Returns: + An exit code. + """ + src_root = os.path.abspath(src_root) + sources = build_utils.ParseGypList(sources_string) + relative_sources = [] + for s in sources: + abs_source = os.path.abspath(s) + if abs_source[:len(src_root)] != src_root: + print ('Error: found source directory not under repository root: %s %s' + % (abs_source, src_root)) + return 1 + rel_source = os.path.relpath(abs_source, src_root) + + relative_sources.append(rel_source) + + with open(sources_file, 'w') as f: + json.dump(relative_sources, f) + + +def _RunInstrumentCommand(command, options, _, option_parser): + """Instruments the classes/jar files using EMMA. + + Args: + command: 'instrument_jar' or 'instrument_classes'. This distinguishes + whether we copy the output from the created lib/ directory, or classes/ + directory. + options: optparse options dictionary. + args: List of extra args from optparse. + option_parser: optparse.OptionParser object. + + Returns: + An exit code. + """ + if not (options.input_path and options.output_path and + options.coverage_file and options.sources_file and options.sources and + options.src_root and options.emma_jar): + option_parser.error('All arguments are required.') + + coverage_file = os.path.join(os.path.dirname(options.output_path), + options.coverage_file) + sources_file = os.path.join(os.path.dirname(options.output_path), + options.sources_file) + if os.path.exists(coverage_file): + os.remove(coverage_file) + temp_dir = tempfile.mkdtemp() + try: + cmd = ['java', '-cp', options.emma_jar, + 'emma', 'instr', + '-ip', options.input_path, + '-ix', options.filter_string, + '-d', temp_dir, + '-out', coverage_file, + '-m', 'fullcopy'] + build_utils.CheckOutput(cmd) + + if command == 'instrument_jar': + for jar in os.listdir(os.path.join(temp_dir, 'lib')): + shutil.copy(os.path.join(temp_dir, 'lib', jar), + options.output_path) + else: # 'instrument_classes' + if os.path.isdir(options.output_path): + shutil.rmtree(options.output_path, ignore_errors=True) + shutil.copytree(os.path.join(temp_dir, 'classes'), + options.output_path) + finally: + shutil.rmtree(temp_dir) + + _CreateSourcesFile(options.sources, sources_file, options.src_root) + + if options.stamp: + build_utils.Touch(options.stamp) + + return 0 + + +CommandFunctionTuple = collections.namedtuple( + 'CommandFunctionTuple', ['add_options_func', 'run_command_func']) +VALID_COMMANDS = { + 'copy': CommandFunctionTuple(_AddCommonOptions, + _RunCopyCommand), + 'instrument_jar': CommandFunctionTuple(_AddInstrumentOptions, + _RunInstrumentCommand), + 'instrument_classes': CommandFunctionTuple(_AddInstrumentOptions, + _RunInstrumentCommand), +} + + +def main(): + option_parser = command_option_parser.CommandOptionParser( + commands_dict=VALID_COMMANDS) + command_option_parser.ParseAndExecute(option_parser) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gyp/finalize_apk.py b/engine/src/flutter/build/android/gyp/finalize_apk.py new file mode 100755 index 0000000000..0a8003508b --- /dev/null +++ b/engine/src/flutter/build/android/gyp/finalize_apk.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# +# Copyright 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. +"""Signs and zipaligns APK. + +""" + +import optparse +import shutil +import sys +import tempfile + +from util import build_utils + +def RenameInflateAndAddPageAlignment( + rezip_apk_jar_path, in_zip_file, out_zip_file): + rezip_apk_cmd = [ + 'java', + '-classpath', + rezip_apk_jar_path, + 'RezipApk', + 'renamealign', + in_zip_file, + out_zip_file, + ] + build_utils.CheckOutput(rezip_apk_cmd) + + +def ReorderAndAlignApk(rezip_apk_jar_path, in_zip_file, out_zip_file): + rezip_apk_cmd = [ + 'java', + '-classpath', + rezip_apk_jar_path, + 'RezipApk', + 'reorder', + in_zip_file, + out_zip_file, + ] + build_utils.CheckOutput(rezip_apk_cmd) + + +def JarSigner(key_path, key_name, key_passwd, unsigned_path, signed_path): + shutil.copy(unsigned_path, signed_path) + sign_cmd = [ + 'jarsigner', + '-sigalg', 'MD5withRSA', + '-digestalg', 'SHA1', + '-keystore', key_path, + '-storepass', key_passwd, + signed_path, + key_name, + ] + build_utils.CheckOutput(sign_cmd) + + +def AlignApk(zipalign_path, unaligned_path, final_path): + align_cmd = [ + zipalign_path, + '-f', '4', # 4 bytes + unaligned_path, + final_path, + ] + build_utils.CheckOutput(align_cmd) + + +def main(): + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option('--rezip-apk-jar-path', + help='Path to the RezipApk jar file.') + parser.add_option('--zipalign-path', help='Path to the zipalign tool.') + parser.add_option('--unsigned-apk-path', help='Path to input unsigned APK.') + parser.add_option('--final-apk-path', + help='Path to output signed and aligned APK.') + parser.add_option('--key-path', help='Path to keystore for signing.') + parser.add_option('--key-passwd', help='Keystore password') + parser.add_option('--key-name', help='Keystore name') + parser.add_option('--stamp', help='Path to touch on success.') + parser.add_option('--load-library-from-zip', type='int', + help='If non-zero, build the APK such that the library can be loaded ' + + 'directly from the zip file using the crazy linker. The library ' + + 'will be renamed, uncompressed and page aligned.') + + options, _ = parser.parse_args() + + FinalizeApk(options) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, build_utils.GetPythonDependencies()) + + if options.stamp: + build_utils.Touch(options.stamp) + + +def FinalizeApk(options): + with tempfile.NamedTemporaryFile() as signed_apk_path_tmp, \ + tempfile.NamedTemporaryFile() as apk_to_sign_tmp: + + if options.load_library_from_zip: + # We alter the name of the library so that the Android Package Manager + # does not extract it into a separate file. This must be done before + # signing, as the filename is part of the signed manifest. At the same + # time we uncompress the library, which is necessary so that it can be + # loaded directly from the APK. + # Move the library to a page boundary by adding a page alignment file. + apk_to_sign = apk_to_sign_tmp.name + RenameInflateAndAddPageAlignment( + options.rezip_apk_jar_path, options.unsigned_apk_path, apk_to_sign) + else: + apk_to_sign = options.unsigned_apk_path + + signed_apk_path = signed_apk_path_tmp.name + JarSigner(options.key_path, options.key_name, options.key_passwd, + apk_to_sign, signed_apk_path) + + if options.load_library_from_zip: + # Reorder the contents of the APK. This re-establishes the canonical + # order which means the library will be back at its page aligned location. + # This step also aligns uncompressed items to 4 bytes. + ReorderAndAlignApk( + options.rezip_apk_jar_path, signed_apk_path, options.final_apk_path) + else: + # Align uncompressed items to 4 bytes + AlignApk(options.zipalign_path, signed_apk_path, options.final_apk_path) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gyp/finalize_splits.py b/engine/src/flutter/build/android/gyp/finalize_splits.py new file mode 100755 index 0000000000..97a65fa920 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/finalize_splits.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# +# Copyright 2015 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. +"""Signs and zipaligns split APKs. + +This script is require only by GYP (not GN). +""" + +import optparse +import sys + +import finalize_apk + +def main(): + parser = optparse.OptionParser() + parser.add_option('--zipalign-path', help='Path to the zipalign tool.') + parser.add_option('--resource-packaged-apk-path', + help='Base path to input .ap_s.') + parser.add_option('--base-output-path', + help='Path to output .apk, minus extension.') + parser.add_option('--key-path', help='Path to keystore for signing.') + parser.add_option('--key-passwd', help='Keystore password') + parser.add_option('--key-name', help='Keystore name') + parser.add_option('--densities', + help='Comma separated list of densities finalize.') + + options, _ = parser.parse_args() + options.load_library_from_zip = 0 + + if options.densities: + for density in options.densities.split(','): + options.unsigned_apk_path = ("%s-%s" % + (options.resource_packaged_apk_path, density)) + options.final_apk_path = ("%s-density-%s.apk" % + (options.base_output_path, density)) + finalize_apk.FinalizeApk(options) + else: + raise Exception('Language splits not yet implemented') + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gyp/find.py b/engine/src/flutter/build/android/gyp/find.py new file mode 100755 index 0000000000..a9f1d49855 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/find.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Finds files in directories. +""" + +import fnmatch +import optparse +import os +import sys + + +def main(argv): + parser = optparse.OptionParser() + parser.add_option('--pattern', default='*', help='File pattern to match.') + options, directories = parser.parse_args(argv) + + for d in directories: + if not os.path.exists(d): + print >> sys.stderr, '%s does not exist' % d + return 1 + for root, _, filenames in os.walk(d): + for f in fnmatch.filter(filenames, options.pattern): + print os.path.join(root, f) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/gcc_preprocess.py b/engine/src/flutter/build/android/gyp/gcc_preprocess.py new file mode 100755 index 0000000000..03becf918f --- /dev/null +++ b/engine/src/flutter/build/android/gyp/gcc_preprocess.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# Copyright 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. + +import optparse +import os +import sys + +from util import build_utils + +def DoGcc(options): + build_utils.MakeDirectory(os.path.dirname(options.output)) + + gcc_cmd = [ 'gcc' ] # invoke host gcc. + if options.defines: + gcc_cmd.extend(sum(map(lambda w: ['-D', w], options.defines), [])) + gcc_cmd.extend([ + '-E', # stop after preprocessing. + '-D', 'ANDROID', # Specify ANDROID define for pre-processor. + '-x', 'c-header', # treat sources as C header files + '-P', # disable line markers, i.e. '#line 309' + '-I', options.include_path, + '-o', options.output, + options.template + ]) + + build_utils.CheckOutput(gcc_cmd) + + +def main(args): + args = build_utils.ExpandFileArgs(args) + + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option('--include-path', help='Include path for gcc.') + parser.add_option('--template', help='Path to template.') + parser.add_option('--output', help='Path for generated file.') + parser.add_option('--stamp', help='Path to touch on success.') + parser.add_option('--defines', help='Pre-defines macros', action='append') + + options, _ = parser.parse_args(args) + + DoGcc(options) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + build_utils.GetPythonDependencies()) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/generate_split_manifest.py b/engine/src/flutter/build/android/gyp/generate_split_manifest.py new file mode 100755 index 0000000000..1a312eda6a --- /dev/null +++ b/engine/src/flutter/build/android/gyp/generate_split_manifest.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# +# Copyright 2015 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. +"""Creates an AndroidManifest.xml for an APK split. + +Given the manifest file for the main APK, generates an AndroidManifest.xml with +the value required for a Split APK (package, versionCode, etc). +""" + +import lxml.etree +import optparse + +from util import build_utils + +MANIFEST_TEMPLATE = """ + + + + + +""" + +def ParseArgs(): + """Parses command line options. + + Returns: + An options object as from optparse.OptionsParser.parse_args() + """ + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option('--main-manifest', help='The main manifest of the app') + parser.add_option('--out-manifest', help='The output manifest') + parser.add_option('--split', help='The name of the split') + parser.add_option( + '--has-code', + action='store_true', + default=False, + help='Whether the split will contain a .dex file') + + (options, args) = parser.parse_args() + + if args: + parser.error('No positional arguments should be given.') + + # Check that required options have been provided. + required_options = ('main_manifest', 'out_manifest', 'split') + build_utils.CheckOptions(options, parser, required=required_options) + + return options + + +def Build(main_manifest, split, has_code): + """Builds a split manifest based on the manifest of the main APK. + + Args: + main_manifest: the XML manifest of the main APK as a string + split: the name of the split as a string + has_code: whether this split APK will contain .dex files + + Returns: + The XML split manifest as a string + """ + + doc = lxml.etree.fromstring(main_manifest) + package = doc.xpath('/manifest/@package')[0] + + return MANIFEST_TEMPLATE % { + 'package': package, + 'split': split.replace('-', '_'), + 'has_code': str(has_code).lower() + } + + +def main(): + options = ParseArgs() + main_manifest = file(options.main_manifest).read() + split_manifest = Build( + main_manifest, + options.split, + options.has_code) + + with file(options.out_manifest, 'w') as f: + f.write(split_manifest) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + [main_manifest] + build_utils.GetPythonDependencies()) + + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/build/android/gyp/generate_v14_compatible_resources.py b/engine/src/flutter/build/android/gyp/generate_v14_compatible_resources.py new file mode 100755 index 0000000000..9c8ff3b4e9 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/generate_v14_compatible_resources.py @@ -0,0 +1,319 @@ +#!/usr/bin/env python +# +# Copyright 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 Android xml resources to API 14 compatible. + +There are two reasons that we cannot just use API 17 attributes, +so we are generating another set of resources by this script. + +1. paddingStart attribute can cause a crash on Galaxy Tab 2. +2. There is a bug that paddingStart does not override paddingLeft on + JB-MR1. This is fixed on JB-MR2. b/8654490 + +Therefore, this resource generation script can be removed when +we drop the support for JB-MR1. + +Please refer to http://crbug.com/235118 for the details. +""" + +import optparse +import os +import re +import shutil +import sys +import xml.dom.minidom as minidom + +from util import build_utils + +# Note that we are assuming 'android:' is an alias of +# the namespace 'http://schemas.android.com/apk/res/android'. + +GRAVITY_ATTRIBUTES = ('android:gravity', 'android:layout_gravity') + +# Almost all the attributes that has "Start" or "End" in +# its name should be mapped. +ATTRIBUTES_TO_MAP = {'paddingStart' : 'paddingLeft', + 'drawableStart' : 'drawableLeft', + 'layout_alignStart' : 'layout_alignLeft', + 'layout_marginStart' : 'layout_marginLeft', + 'layout_alignParentStart' : 'layout_alignParentLeft', + 'layout_toStartOf' : 'layout_toLeftOf', + 'paddingEnd' : 'paddingRight', + 'drawableEnd' : 'drawableRight', + 'layout_alignEnd' : 'layout_alignRight', + 'layout_marginEnd' : 'layout_marginRight', + 'layout_alignParentEnd' : 'layout_alignParentRight', + 'layout_toEndOf' : 'layout_toRightOf'} + +ATTRIBUTES_TO_MAP = dict(['android:' + k, 'android:' + v] for k, v + in ATTRIBUTES_TO_MAP.iteritems()) + +ATTRIBUTES_TO_MAP_REVERSED = dict([v, k] for k, v + in ATTRIBUTES_TO_MAP.iteritems()) + + +def IterateXmlElements(node): + """minidom helper function that iterates all the element nodes. + Iteration order is pre-order depth-first.""" + if node.nodeType == node.ELEMENT_NODE: + yield node + for child_node in node.childNodes: + for child_node_element in IterateXmlElements(child_node): + yield child_node_element + + +def ParseAndReportErrors(filename): + try: + return minidom.parse(filename) + except Exception: + import traceback + traceback.print_exc() + sys.stderr.write('Failed to parse XML file: %s\n' % filename) + sys.exit(1) + + +def AssertNotDeprecatedAttribute(name, value, filename): + """Raises an exception if the given attribute is deprecated.""" + msg = None + if name in ATTRIBUTES_TO_MAP_REVERSED: + msg = '{0} should use {1} instead of {2}'.format(filename, + ATTRIBUTES_TO_MAP_REVERSED[name], name) + elif name in GRAVITY_ATTRIBUTES and ('left' in value or 'right' in value): + msg = '{0} should use start/end instead of left/right for {1}'.format( + filename, name) + + if msg: + msg += ('\nFor background, see: http://android-developers.blogspot.com/' + '2013/03/native-rtl-support-in-android-42.html\n' + 'If you have a legitimate need for this attribute, discuss with ' + 'kkimlabs@chromium.org or newt@chromium.org') + raise Exception(msg) + + +def WriteDomToFile(dom, filename): + """Write the given dom to filename.""" + build_utils.MakeDirectory(os.path.dirname(filename)) + with open(filename, 'w') as f: + dom.writexml(f, '', ' ', '\n', encoding='utf-8') + + +def HasStyleResource(dom): + """Return True if the dom is a style resource, False otherwise.""" + root_node = IterateXmlElements(dom).next() + return bool(root_node.nodeName == 'resources' and + list(root_node.getElementsByTagName('style'))) + + +def ErrorIfStyleResourceExistsInDir(input_dir): + """If a style resource is in input_dir, raises an exception.""" + for input_filename in build_utils.FindInDirectory(input_dir, '*.xml'): + dom = ParseAndReportErrors(input_filename) + if HasStyleResource(dom): + raise Exception('error: style file ' + input_filename + + ' should be under ' + input_dir + + '-v17 directory. Please refer to ' + 'http://crbug.com/243952 for the details.') + + +def GenerateV14LayoutResourceDom(dom, filename, assert_not_deprecated=True): + """Convert layout resource to API 14 compatible layout resource. + + Args: + dom: Parsed minidom object to be modified. + filename: Filename that the DOM was parsed from. + assert_not_deprecated: Whether deprecated attributes (e.g. paddingLeft) will + cause an exception to be thrown. + + Returns: + True if dom is modified, False otherwise. + """ + is_modified = False + + # Iterate all the elements' attributes to find attributes to convert. + for element in IterateXmlElements(dom): + for name, value in list(element.attributes.items()): + # Convert any API 17 Start/End attributes to Left/Right attributes. + # For example, from paddingStart="10dp" to paddingLeft="10dp" + # Note: gravity attributes are not necessary to convert because + # start/end values are backward-compatible. Explained at + # https://plus.sandbox.google.com/+RomanNurik/posts/huuJd8iVVXY?e=Showroom + if name in ATTRIBUTES_TO_MAP: + element.setAttribute(ATTRIBUTES_TO_MAP[name], value) + del element.attributes[name] + is_modified = True + elif assert_not_deprecated: + AssertNotDeprecatedAttribute(name, value, filename) + + return is_modified + + +def GenerateV14StyleResourceDom(dom, filename, assert_not_deprecated=True): + """Convert style resource to API 14 compatible style resource. + + Args: + dom: Parsed minidom object to be modified. + filename: Filename that the DOM was parsed from. + assert_not_deprecated: Whether deprecated attributes (e.g. paddingLeft) will + cause an exception to be thrown. + + Returns: + True if dom is modified, False otherwise. + """ + is_modified = False + + for style_element in dom.getElementsByTagName('style'): + for item_element in style_element.getElementsByTagName('item'): + name = item_element.attributes['name'].value + value = item_element.childNodes[0].nodeValue + if name in ATTRIBUTES_TO_MAP: + item_element.attributes['name'].value = ATTRIBUTES_TO_MAP[name] + is_modified = True + elif assert_not_deprecated: + AssertNotDeprecatedAttribute(name, value, filename) + + return is_modified + + +def GenerateV14LayoutResource(input_filename, output_v14_filename, + output_v17_filename): + """Convert API 17 layout resource to API 14 compatible layout resource. + + It's mostly a simple replacement, s/Start/Left s/End/Right, + on the attribute names. + If the generated resource is identical to the original resource, + don't do anything. If not, write the generated resource to + output_v14_filename, and copy the original resource to output_v17_filename. + """ + dom = ParseAndReportErrors(input_filename) + is_modified = GenerateV14LayoutResourceDom(dom, input_filename) + + if is_modified: + # Write the generated resource. + WriteDomToFile(dom, output_v14_filename) + + # Copy the original resource. + build_utils.MakeDirectory(os.path.dirname(output_v17_filename)) + shutil.copy2(input_filename, output_v17_filename) + + +def GenerateV14StyleResource(input_filename, output_v14_filename): + """Convert API 17 style resources to API 14 compatible style resource. + + Write the generated style resource to output_v14_filename. + It's mostly a simple replacement, s/Start/Left s/End/Right, + on the attribute names. + """ + dom = ParseAndReportErrors(input_filename) + GenerateV14StyleResourceDom(dom, input_filename) + + # Write the generated resource. + WriteDomToFile(dom, output_v14_filename) + + +def GenerateV14LayoutResourcesInDir(input_dir, output_v14_dir, output_v17_dir): + """Convert layout resources to API 14 compatible resources in input_dir.""" + for input_filename in build_utils.FindInDirectory(input_dir, '*.xml'): + rel_filename = os.path.relpath(input_filename, input_dir) + output_v14_filename = os.path.join(output_v14_dir, rel_filename) + output_v17_filename = os.path.join(output_v17_dir, rel_filename) + GenerateV14LayoutResource(input_filename, output_v14_filename, + output_v17_filename) + + +def GenerateV14StyleResourcesInDir(input_dir, output_v14_dir): + """Convert style resources to API 14 compatible resources in input_dir.""" + for input_filename in build_utils.FindInDirectory(input_dir, '*.xml'): + rel_filename = os.path.relpath(input_filename, input_dir) + output_v14_filename = os.path.join(output_v14_dir, rel_filename) + GenerateV14StyleResource(input_filename, output_v14_filename) + + +def ParseArgs(): + """Parses command line options. + + Returns: + An options object as from optparse.OptionsParser.parse_args() + """ + parser = optparse.OptionParser() + parser.add_option('--res-dir', + help='directory containing resources ' + 'used to generate v14 compatible resources') + parser.add_option('--res-v14-compatibility-dir', + help='output directory into which ' + 'v14 compatible resources will be generated') + parser.add_option('--stamp', help='File to touch on success') + + options, args = parser.parse_args() + + if args: + parser.error('No positional arguments should be given.') + + # Check that required options have been provided. + required_options = ('res_dir', 'res_v14_compatibility_dir') + build_utils.CheckOptions(options, parser, required=required_options) + return options + +def GenerateV14Resources(res_dir, res_v14_dir): + for name in os.listdir(res_dir): + if not os.path.isdir(os.path.join(res_dir, name)): + continue + + dir_pieces = name.split('-') + resource_type = dir_pieces[0] + qualifiers = dir_pieces[1:] + + api_level_qualifier_index = -1 + api_level_qualifier = '' + for index, qualifier in enumerate(qualifiers): + if re.match('v[0-9]+$', qualifier): + api_level_qualifier_index = index + api_level_qualifier = qualifier + break + + # Android pre-v17 API doesn't support RTL. Skip. + if 'ldrtl' in qualifiers: + continue + + input_dir = os.path.abspath(os.path.join(res_dir, name)) + + # We also need to copy the original v17 resource to *-v17 directory + # because the generated v14 resource will hide the original resource. + output_v14_dir = os.path.join(res_v14_dir, name) + output_v17_dir = os.path.join(res_v14_dir, name + '-v17') + + # We only convert layout resources under layout*/, xml*/, + # and style resources under values*/. + if resource_type in ('layout', 'xml'): + if not api_level_qualifier: + GenerateV14LayoutResourcesInDir(input_dir, output_v14_dir, + output_v17_dir) + elif resource_type == 'values': + if api_level_qualifier == 'v17': + output_qualifiers = qualifiers[:] + del output_qualifiers[api_level_qualifier_index] + output_v14_dir = os.path.join(res_v14_dir, + '-'.join([resource_type] + + output_qualifiers)) + GenerateV14StyleResourcesInDir(input_dir, output_v14_dir) + elif not api_level_qualifier: + ErrorIfStyleResourceExistsInDir(input_dir) + +def main(): + options = ParseArgs() + + res_v14_dir = options.res_v14_compatibility_dir + + build_utils.DeleteDirectory(res_v14_dir) + build_utils.MakeDirectory(res_v14_dir) + + GenerateV14Resources(options.res_dir, res_v14_dir) + + if options.stamp: + build_utils.Touch(options.stamp) + +if __name__ == '__main__': + sys.exit(main()) + diff --git a/engine/src/flutter/build/android/gyp/get_device_configuration.py b/engine/src/flutter/build/android/gyp/get_device_configuration.py new file mode 100755 index 0000000000..390eb2ffcb --- /dev/null +++ b/engine/src/flutter/build/android/gyp/get_device_configuration.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# +# Copyright 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. + +"""Gets and writes the configurations of the attached devices. + +This configuration is used by later build steps to determine which devices to +install to and what needs to be installed to those devices. +""" + +import optparse +import sys + +from util import build_utils +from util import build_device + + +def main(argv): + parser = optparse.OptionParser() + parser.add_option('--stamp', action='store') + parser.add_option('--output', action='store') + options, _ = parser.parse_args(argv) + + devices = build_device.GetAttachedDevices() + + device_configurations = [] + for d in devices: + configuration, is_online, has_root = ( + build_device.GetConfigurationForDevice(d)) + + if not is_online: + build_utils.PrintBigWarning( + '%s is not online. Skipping managed install for this device. ' + 'Try rebooting the device to fix this warning.' % d) + continue + + if not has_root: + build_utils.PrintBigWarning( + '"adb root" failed on device: %s\n' + 'Skipping managed install for this device.' + % configuration['description']) + continue + + device_configurations.append(configuration) + + if len(device_configurations) == 0: + build_utils.PrintBigWarning( + 'No valid devices attached. Skipping managed install steps.') + elif len(devices) > 1: + # Note that this checks len(devices) and not len(device_configurations). + # This way, any time there are multiple devices attached it is + # explicitly stated which device we will install things to even if all but + # one device were rejected for other reasons (e.g. two devices attached, + # one w/o root). + build_utils.PrintBigWarning( + 'Multiple devices attached. ' + 'Installing to the preferred device: ' + '%(id)s (%(description)s)' % (device_configurations[0])) + + + build_device.WriteConfigurations(device_configurations, options.output) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/gyp/insert_chromium_version.py b/engine/src/flutter/build/android/gyp/insert_chromium_version.py new file mode 100755 index 0000000000..171f9d41b8 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/insert_chromium_version.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Insert a version string into a library as a section '.chromium.version'. +""" + +import optparse +import os +import sys +import tempfile + +from util import build_utils + +def InsertChromiumVersion(android_objcopy, + library_path, + version_string): + # Remove existing .chromium.version section from .so + objcopy_command = [android_objcopy, + '--remove-section=.chromium.version', + library_path] + build_utils.CheckOutput(objcopy_command) + + # Add a .chromium.version section. + with tempfile.NamedTemporaryFile() as stream: + stream.write(version_string) + stream.flush() + objcopy_command = [android_objcopy, + '--add-section', '.chromium.version=%s' % stream.name, + library_path] + build_utils.CheckOutput(objcopy_command) + +def main(args): + args = build_utils.ExpandFileArgs(args) + parser = optparse.OptionParser() + + parser.add_option('--android-objcopy', + help='Path to the toolchain\'s objcopy binary') + parser.add_option('--stripped-libraries-dir', + help='Directory of native libraries') + parser.add_option('--libraries', + help='List of libraries') + parser.add_option('--version-string', + help='Version string to be inserted') + parser.add_option('--stamp', help='Path to touch on success') + + options, _ = parser.parse_args(args) + libraries = build_utils.ParseGypList(options.libraries) + + for library in libraries: + library_path = os.path.join(options.stripped_libraries_dir, library) + + InsertChromiumVersion(options.android_objcopy, + library_path, + options.version_string) + + if options.stamp: + build_utils.Touch(options.stamp) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/jar.py b/engine/src/flutter/build/android/gyp/jar.py new file mode 100755 index 0000000000..48abf5edb7 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/jar.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# Copyright 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. + +import fnmatch +import optparse +import os +import sys + +from util import build_utils +from util import md5_check + + +def Jar(class_files, classes_dir, jar_path, manifest_file=None): + jar_path = os.path.abspath(jar_path) + + # The paths of the files in the jar will be the same as they are passed in to + # the command. Because of this, the command should be run in + # options.classes_dir so the .class file paths in the jar are correct. + jar_cwd = classes_dir + class_files_rel = [os.path.relpath(f, jar_cwd) for f in class_files] + jar_cmd = ['jar', 'cf0', jar_path] + if manifest_file: + jar_cmd[1] += 'm' + jar_cmd.append(os.path.abspath(manifest_file)) + jar_cmd.extend(class_files_rel) + + with build_utils.TempDir() as temp_dir: + empty_file = os.path.join(temp_dir, '.empty') + build_utils.Touch(empty_file) + jar_cmd.append(os.path.relpath(empty_file, jar_cwd)) + record_path = '%s.md5.stamp' % jar_path + md5_check.CallAndRecordIfStale( + lambda: build_utils.CheckOutput(jar_cmd, cwd=jar_cwd), + record_path=record_path, + input_paths=class_files, + input_strings=jar_cmd, + force=not os.path.exists(jar_path), + ) + + build_utils.Touch(jar_path, fail_if_missing=True) + + +def JarDirectory(classes_dir, excluded_classes, jar_path, manifest_file=None): + class_files = build_utils.FindInDirectory(classes_dir, '*.class') + for exclude in excluded_classes: + class_files = filter( + lambda f: not fnmatch.fnmatch(f, exclude), class_files) + + Jar(class_files, classes_dir, jar_path, manifest_file=manifest_file) + + +def main(): + parser = optparse.OptionParser() + parser.add_option('--classes-dir', help='Directory containing .class files.') + parser.add_option('--jar-path', help='Jar output path.') + parser.add_option('--excluded-classes', + help='List of .class file patterns to exclude from the jar.') + parser.add_option('--stamp', help='Path to touch on success.') + + options, _ = parser.parse_args() + + if options.excluded_classes: + excluded_classes = build_utils.ParseGypList(options.excluded_classes) + else: + excluded_classes = [] + JarDirectory(options.classes_dir, + excluded_classes, + options.jar_path) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main()) + diff --git a/engine/src/flutter/build/android/gyp/jar_toc.py b/engine/src/flutter/build/android/gyp/jar_toc.py new file mode 100755 index 0000000000..b7fe099598 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/jar_toc.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# +# Copyright 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. + +"""Creates a TOC file from a Java jar. + +The TOC file contains the non-package API of the jar. This includes all +public/protected/package classes/functions/members and the values of static +final variables (members with package access are kept because in some cases we +have multiple libraries with the same package, particularly test+non-test). Some +other information (major/minor javac version) is also included. + +This TOC file then can be used to determine if a dependent library should be +rebuilt when this jar changes. I.e. any change to the jar that would require a +rebuild, will have a corresponding change in the TOC file. +""" + +import optparse +import os +import re +import sys +import zipfile + +from util import build_utils +from util import md5_check + + +def GetClassesInZipFile(zip_file): + classes = [] + files = zip_file.namelist() + for f in files: + if f.endswith('.class'): + # f is of the form org/chromium/base/Class$Inner.class + classes.append(f.replace('/', '.')[:-6]) + return classes + + +def CallJavap(classpath, classes): + output = '' + for i in range(0, len(classes), 2000): + javap_cmd = [ + 'javap', + '-package', # Show public/protected/package. + # -verbose is required to get constant values (which can be inlined in + # dependents). + '-verbose', + '-classpath', classpath + ] + classes[i:i+2000] + output += build_utils.CheckOutput(javap_cmd) + return output + + +def ExtractToc(disassembled_classes): + # javap output is structured by indent (2-space) levels. + good_patterns = [ + '^[^ ]', # This includes all class/function/member signatures. + '^ SourceFile:', + '^ minor version:', + '^ major version:', + '^ Constant value:', + ] + bad_patterns = [ + '^const #', # Matches the constant pool (i.e. literals used in the class). + ] + + def JavapFilter(line): + return (re.match('|'.join(good_patterns), line) and + not re.match('|'.join(bad_patterns), line)) + toc = filter(JavapFilter, disassembled_classes.split('\n')) + + return '\n'.join(toc) + + +def UpdateToc(jar_path, toc_path): + classes = GetClassesInZipFile(zipfile.ZipFile(jar_path)) + toc = '' + if len(classes) != 0: + javap_output = CallJavap(classpath=jar_path, classes=classes) + toc = ExtractToc(javap_output) + + with open(toc_path, 'w') as tocfile: + tocfile.write(toc) + + +def DoJarToc(options): + jar_path = options.jar_path + toc_path = options.toc_path + record_path = '%s.md5.stamp' % toc_path + md5_check.CallAndRecordIfStale( + lambda: UpdateToc(jar_path, toc_path), + record_path=record_path, + input_paths=[jar_path], + force=not os.path.exists(toc_path), + ) + build_utils.Touch(toc_path, fail_if_missing=True) + + +def main(): + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option('--jar-path', help='Input .jar path.') + parser.add_option('--toc-path', help='Output .jar.TOC path.') + parser.add_option('--stamp', help='Path to touch on success.') + + options, _ = parser.parse_args() + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + build_utils.GetPythonDependencies()) + + DoJarToc(options) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + build_utils.GetPythonDependencies()) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gyp/java_cpp_enum.py b/engine/src/flutter/build/android/gyp/java_cpp_enum.py new file mode 100755 index 0000000000..c2f1764b1b --- /dev/null +++ b/engine/src/flutter/build/android/gyp/java_cpp_enum.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +import collections +import re +import optparse +import os +from string import Template +import sys + +from util import build_utils + +# List of C++ types that are compatible with the Java code generated by this +# script. +# +# This script can parse .idl files however, at present it ignores special +# rules such as [cpp_enum_prefix_override="ax_attr"]. +ENUM_FIXED_TYPE_WHITELIST = ['char', 'unsigned char', + 'short', 'unsigned short', + 'int', 'int8_t', 'int16_t', 'int32_t', 'uint8_t', 'uint16_t'] + +class EnumDefinition(object): + def __init__(self, original_enum_name=None, class_name_override=None, + enum_package=None, entries=None, fixed_type=None): + self.original_enum_name = original_enum_name + self.class_name_override = class_name_override + self.enum_package = enum_package + self.entries = collections.OrderedDict(entries or []) + self.prefix_to_strip = None + self.fixed_type = fixed_type + + def AppendEntry(self, key, value): + if key in self.entries: + raise Exception('Multiple definitions of key %s found.' % key) + self.entries[key] = value + + @property + def class_name(self): + return self.class_name_override or self.original_enum_name + + def Finalize(self): + self._Validate() + self._AssignEntryIndices() + self._StripPrefix() + + def _Validate(self): + assert self.class_name + assert self.enum_package + assert self.entries + if self.fixed_type and self.fixed_type not in ENUM_FIXED_TYPE_WHITELIST: + raise Exception('Fixed type %s for enum %s not whitelisted.' % + (self.fixed_type, self.class_name)) + + def _AssignEntryIndices(self): + # Enums, if given no value, are given the value of the previous enum + 1. + if not all(self.entries.values()): + prev_enum_value = -1 + for key, value in self.entries.iteritems(): + if not value: + self.entries[key] = prev_enum_value + 1 + elif value in self.entries: + self.entries[key] = self.entries[value] + else: + try: + self.entries[key] = int(value) + except ValueError: + raise Exception('Could not interpret integer from enum value "%s" ' + 'for key %s.' % (value, key)) + prev_enum_value = self.entries[key] + + + def _StripPrefix(self): + prefix_to_strip = self.prefix_to_strip + if not prefix_to_strip: + prefix_to_strip = self.original_enum_name + prefix_to_strip = re.sub('(?!^)([A-Z]+)', r'_\1', prefix_to_strip).upper() + prefix_to_strip += '_' + if not all([w.startswith(prefix_to_strip) for w in self.entries.keys()]): + prefix_to_strip = '' + + entries = collections.OrderedDict() + for (k, v) in self.entries.iteritems(): + stripped_key = k.replace(prefix_to_strip, '', 1) + if isinstance(v, basestring): + stripped_value = v.replace(prefix_to_strip, '', 1) + else: + stripped_value = v + entries[stripped_key] = stripped_value + + self.entries = entries + +class DirectiveSet(object): + class_name_override_key = 'CLASS_NAME_OVERRIDE' + enum_package_key = 'ENUM_PACKAGE' + prefix_to_strip_key = 'PREFIX_TO_STRIP' + + known_keys = [class_name_override_key, enum_package_key, prefix_to_strip_key] + + def __init__(self): + self._directives = {} + + def Update(self, key, value): + if key not in DirectiveSet.known_keys: + raise Exception("Unknown directive: " + key) + self._directives[key] = value + + @property + def empty(self): + return len(self._directives) == 0 + + def UpdateDefinition(self, definition): + definition.class_name_override = self._directives.get( + DirectiveSet.class_name_override_key, '') + definition.enum_package = self._directives.get( + DirectiveSet.enum_package_key) + definition.prefix_to_strip = self._directives.get( + DirectiveSet.prefix_to_strip_key) + + +class HeaderParser(object): + single_line_comment_re = re.compile(r'\s*//') + multi_line_comment_start_re = re.compile(r'\s*/\*') + enum_line_re = re.compile(r'^\s*(\w+)(\s*\=\s*([^,\n]+))?,?') + enum_end_re = re.compile(r'^\s*}\s*;\.*$') + generator_directive_re = re.compile( + r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*([\.\w]+)$') + multi_line_generator_directive_start_re = re.compile( + r'^\s*//\s+GENERATED_JAVA_(\w+)\s*:\s*\(([\.\w]*)$') + multi_line_directive_continuation_re = re.compile( + r'^\s*//\s+([\.\w]+)$') + multi_line_directive_end_re = re.compile( + r'^\s*//\s+([\.\w]*)\)$') + + optional_class_or_struct_re = r'(class|struct)?' + enum_name_re = r'(\w+)' + optional_fixed_type_re = r'(\:\s*(\w+\s*\w+?))?' + enum_start_re = re.compile(r'^\s*(?:\[cpp.*\])?\s*enum\s+' + + optional_class_or_struct_re + '\s*' + enum_name_re + '\s*' + + optional_fixed_type_re + '\s*{\s*$') + + def __init__(self, lines, path=None): + self._lines = lines + self._path = path + self._enum_definitions = [] + self._in_enum = False + self._current_definition = None + self._generator_directives = DirectiveSet() + self._multi_line_generator_directive = None + + def _ApplyGeneratorDirectives(self): + self._generator_directives.UpdateDefinition(self._current_definition) + self._generator_directives = DirectiveSet() + + def ParseDefinitions(self): + for line in self._lines: + self._ParseLine(line) + return self._enum_definitions + + def _ParseLine(self, line): + if self._multi_line_generator_directive: + self._ParseMultiLineDirectiveLine(line) + elif not self._in_enum: + self._ParseRegularLine(line) + else: + self._ParseEnumLine(line) + + def _ParseEnumLine(self, line): + if HeaderParser.single_line_comment_re.match(line): + return + if HeaderParser.multi_line_comment_start_re.match(line): + raise Exception('Multi-line comments in enums are not supported in ' + + self._path) + enum_end = HeaderParser.enum_end_re.match(line) + enum_entry = HeaderParser.enum_line_re.match(line) + if enum_end: + self._ApplyGeneratorDirectives() + self._current_definition.Finalize() + self._enum_definitions.append(self._current_definition) + self._in_enum = False + elif enum_entry: + enum_key = enum_entry.groups()[0] + enum_value = enum_entry.groups()[2] + self._current_definition.AppendEntry(enum_key, enum_value) + + def _ParseMultiLineDirectiveLine(self, line): + multi_line_directive_continuation = ( + HeaderParser.multi_line_directive_continuation_re.match(line)) + multi_line_directive_end = ( + HeaderParser.multi_line_directive_end_re.match(line)) + + if multi_line_directive_continuation: + value_cont = multi_line_directive_continuation.groups()[0] + self._multi_line_generator_directive[1].append(value_cont) + elif multi_line_directive_end: + directive_name = self._multi_line_generator_directive[0] + directive_value = "".join(self._multi_line_generator_directive[1]) + directive_value += multi_line_directive_end.groups()[0] + self._multi_line_generator_directive = None + self._generator_directives.Update(directive_name, directive_value) + else: + raise Exception('Malformed multi-line directive declaration in ' + + self._path) + + def _ParseRegularLine(self, line): + enum_start = HeaderParser.enum_start_re.match(line) + generator_directive = HeaderParser.generator_directive_re.match(line) + multi_line_generator_directive_start = ( + HeaderParser.multi_line_generator_directive_start_re.match(line)) + + if generator_directive: + directive_name = generator_directive.groups()[0] + directive_value = generator_directive.groups()[1] + self._generator_directives.Update(directive_name, directive_value) + elif multi_line_generator_directive_start: + directive_name = multi_line_generator_directive_start.groups()[0] + directive_value = multi_line_generator_directive_start.groups()[1] + self._multi_line_generator_directive = (directive_name, [directive_value]) + elif enum_start: + if self._generator_directives.empty: + return + self._current_definition = EnumDefinition( + original_enum_name=enum_start.groups()[1], + fixed_type=enum_start.groups()[3]) + self._in_enum = True + +def GetScriptName(): + script_components = os.path.abspath(sys.argv[0]).split(os.path.sep) + build_index = script_components.index('build') + return os.sep.join(script_components[build_index:]) + + +def DoGenerate(output_dir, source_paths, print_output_only=False): + output_paths = [] + for source_path in source_paths: + enum_definitions = DoParseHeaderFile(source_path) + if not enum_definitions: + raise Exception('No enums found in %s\n' + 'Did you forget prefixing enums with ' + '"// GENERATED_JAVA_ENUM_PACKAGE: foo"?' % + source_path) + for enum_definition in enum_definitions: + package_path = enum_definition.enum_package.replace('.', os.path.sep) + file_name = enum_definition.class_name + '.java' + output_path = os.path.join(output_dir, package_path, file_name) + output_paths.append(output_path) + if not print_output_only: + build_utils.MakeDirectory(os.path.dirname(output_path)) + DoWriteOutput(source_path, output_path, enum_definition) + return output_paths + + +def DoParseHeaderFile(path): + with open(path) as f: + return HeaderParser(f.readlines(), path).ParseDefinitions() + + +def GenerateOutput(source_path, enum_definition): + template = Template(""" +// Copyright 2014 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. + +// This file is autogenerated by +// ${SCRIPT_NAME} +// From +// ${SOURCE_PATH} + +package ${PACKAGE}; + +public class ${CLASS_NAME} { +${ENUM_ENTRIES} +} +""") + + enum_template = Template(' public static final int ${NAME} = ${VALUE};') + enum_entries_string = [] + for enum_name, enum_value in enum_definition.entries.iteritems(): + values = { + 'NAME': enum_name, + 'VALUE': enum_value, + } + enum_entries_string.append(enum_template.substitute(values)) + enum_entries_string = '\n'.join(enum_entries_string) + + values = { + 'CLASS_NAME': enum_definition.class_name, + 'ENUM_ENTRIES': enum_entries_string, + 'PACKAGE': enum_definition.enum_package, + 'SCRIPT_NAME': GetScriptName(), + 'SOURCE_PATH': source_path, + } + return template.substitute(values) + + +def DoWriteOutput(source_path, output_path, enum_definition): + with open(output_path, 'w') as out_file: + out_file.write(GenerateOutput(source_path, enum_definition)) + +def AssertFilesList(output_paths, assert_files_list): + actual = set(output_paths) + expected = set(assert_files_list) + if not actual == expected: + need_to_add = list(actual - expected) + need_to_remove = list(expected - actual) + raise Exception('Output files list does not match expectations. Please ' + 'add %s and remove %s.' % (need_to_add, need_to_remove)) + +def DoMain(argv): + usage = 'usage: %prog [options] output_dir input_file(s)...' + parser = optparse.OptionParser(usage=usage) + + parser.add_option('--assert_file', action="append", default=[], + dest="assert_files_list", help='Assert that the given ' + 'file is an output. There can be multiple occurrences of ' + 'this flag.') + parser.add_option('--print_output_only', help='Only print output paths.', + action='store_true') + parser.add_option('--verbose', help='Print more information.', + action='store_true') + + options, args = parser.parse_args(argv) + if len(args) < 2: + parser.error('Need to specify output directory and at least one input file') + output_paths = DoGenerate(args[0], args[1:], + print_output_only=options.print_output_only) + + if options.assert_files_list: + AssertFilesList(output_paths, options.assert_files_list) + + if options.verbose: + print 'Output paths:' + print '\n'.join(output_paths) + + return ' '.join(output_paths) + +if __name__ == '__main__': + DoMain(sys.argv[1:]) diff --git a/engine/src/flutter/build/android/gyp/java_cpp_enum_tests.py b/engine/src/flutter/build/android/gyp/java_cpp_enum_tests.py new file mode 100755 index 0000000000..44f9766c82 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/java_cpp_enum_tests.py @@ -0,0 +1,436 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +"""Tests for enum_preprocess.py. + +This test suite containss various tests for the C++ -> Java enum generator. +""" + +import collections +import optparse +import os +import sys +import unittest + +import java_cpp_enum +from java_cpp_enum import EnumDefinition, GenerateOutput, GetScriptName +from java_cpp_enum import HeaderParser + +sys.path.append(os.path.join(os.path.dirname(__file__), "gyp")) +from util import build_utils + +class TestPreprocess(unittest.TestCase): + def testOutput(self): + definition = EnumDefinition(original_enum_name='ClassName', + enum_package='some.package', + entries=[('E1', 1), ('E2', '2 << 2')]) + output = GenerateOutput('path/to/file', definition) + expected = """ +// Copyright 2014 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. + +// This file is autogenerated by +// %s +// From +// path/to/file + +package some.package; + +public class ClassName { + public static final int E1 = 1; + public static final int E2 = 2 << 2; +} +""" + self.assertEqual(expected % GetScriptName(), output) + + def testParseSimpleEnum(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum EnumName { + VALUE_ZERO, + VALUE_ONE, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('EnumName', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual(collections.OrderedDict([('VALUE_ZERO', 0), + ('VALUE_ONE', 1)]), + definition.entries) + + def testParseBitShifts(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum EnumName { + VALUE_ZERO = 1 << 0, + VALUE_ONE = 1 << 1, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('EnumName', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual(collections.OrderedDict([('VALUE_ZERO', '1 << 0'), + ('VALUE_ONE', '1 << 1')]), + definition.entries) + + def testParseClassNameOverride(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + // GENERATED_JAVA_CLASS_NAME_OVERRIDE: OverrideName + enum EnumName { + FOO + }; + + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + // GENERATED_JAVA_CLASS_NAME_OVERRIDE: OtherOverride + enum PrefixTest { + PREFIX_TEST_A, + PREFIX_TEST_B, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(2, len(definitions)) + definition = definitions[0] + self.assertEqual('OverrideName', definition.class_name) + + definition = definitions[1] + self.assertEqual('OtherOverride', definition.class_name) + self.assertEqual(collections.OrderedDict([('A', 0), + ('B', 1)]), + definition.entries) + + def testParseTwoEnums(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum EnumOne { + ENUM_ONE_A = 1, + // Comment there + ENUM_ONE_B = A, + }; + + enum EnumIgnore { + C, D, E + }; + + // GENERATED_JAVA_ENUM_PACKAGE: other.package + // GENERATED_JAVA_PREFIX_TO_STRIP: P_ + enum EnumTwo { + P_A, + P_B + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(2, len(definitions)) + definition = definitions[0] + self.assertEqual('EnumOne', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual(collections.OrderedDict([('A', '1'), + ('B', 'A')]), + definition.entries) + + definition = definitions[1] + self.assertEqual('EnumTwo', definition.class_name) + self.assertEqual('other.package', definition.enum_package) + self.assertEqual(collections.OrderedDict([('A', 0), + ('B', 1)]), + definition.entries) + + def testParseThrowsOnUnknownDirective(self): + test_data = """ + // GENERATED_JAVA_UNKNOWN: Value + enum EnumName { + VALUE_ONE, + }; + """.split('\n') + with self.assertRaises(Exception): + HeaderParser(test_data).ParseDefinitions() + + def testParseReturnsEmptyListWithoutDirectives(self): + test_data = """ + enum EnumName { + VALUE_ONE, + }; + """.split('\n') + self.assertEqual([], HeaderParser(test_data).ParseDefinitions()) + + def testParseEnumClass(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum class Foo { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('Foo', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual(collections.OrderedDict([('A', 0)]), + definition.entries) + + def testParseEnumStruct(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum struct Foo { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('Foo', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual(collections.OrderedDict([('A', 0)]), + definition.entries) + + def testParseFixedTypeEnum(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum Foo : int { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('Foo', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual('int', definition.fixed_type) + self.assertEqual(collections.OrderedDict([('A', 0)]), + definition.entries) + + def testParseFixedTypeEnumClass(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum class Foo: unsigned short { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual(1, len(definitions)) + definition = definitions[0] + self.assertEqual('Foo', definition.class_name) + self.assertEqual('test.namespace', definition.enum_package) + self.assertEqual('unsigned short', definition.fixed_type) + self.assertEqual(collections.OrderedDict([('A', 0)]), + definition.entries) + + def testParseUnknownFixedTypeRaises(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: test.namespace + enum class Foo: foo_type { + FOO_A, + }; + """.split('\n') + with self.assertRaises(Exception): + HeaderParser(test_data).ParseDefinitions() + + def testParseSimpleMultiLineDirective(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: ( + // test.namespace) + // GENERATED_JAVA_CLASS_NAME_OVERRIDE: Bar + enum Foo { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual('test.namespace', definitions[0].enum_package) + self.assertEqual('Bar', definitions[0].class_name) + + def testParseMultiLineDirective(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: (te + // st.name + // space) + enum Foo { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual('test.namespace', definitions[0].enum_package) + + def testParseMultiLineDirectiveWithOtherDirective(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: ( + // test.namespace) + // GENERATED_JAVA_CLASS_NAME_OVERRIDE: ( + // Ba + // r + // ) + enum Foo { + FOO_A, + }; + """.split('\n') + definitions = HeaderParser(test_data).ParseDefinitions() + self.assertEqual('test.namespace', definitions[0].enum_package) + self.assertEqual('Bar', definitions[0].class_name) + + def testParseMalformedMultiLineDirectiveWithOtherDirective(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: ( + // test.name + // space + // GENERATED_JAVA_CLASS_NAME_OVERRIDE: Bar + enum Foo { + FOO_A, + }; + """.split('\n') + with self.assertRaises(Exception): + HeaderParser(test_data).ParseDefinitions() + + def testParseMalformedMultiLineDirective(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: ( + // test.name + // space + enum Foo { + FOO_A, + }; + """.split('\n') + with self.assertRaises(Exception): + HeaderParser(test_data).ParseDefinitions() + + def testParseMalformedMultiLineDirectiveShort(self): + test_data = """ + // GENERATED_JAVA_ENUM_PACKAGE: ( + enum Foo { + FOO_A, + }; + """.split('\n') + with self.assertRaises(Exception): + HeaderParser(test_data).ParseDefinitions() + + def testEnumValueAssignmentNoneDefined(self): + definition = EnumDefinition(original_enum_name='c', enum_package='p') + definition.AppendEntry('A', None) + definition.AppendEntry('B', None) + definition.AppendEntry('C', None) + definition.Finalize() + self.assertEqual(collections.OrderedDict([('A', 0), + ('B', 1), + ('C', 2)]), + definition.entries) + + def testEnumValueAssignmentAllDefined(self): + definition = EnumDefinition(original_enum_name='c', enum_package='p') + definition.AppendEntry('A', '1') + definition.AppendEntry('B', '2') + definition.AppendEntry('C', '3') + definition.Finalize() + self.assertEqual(collections.OrderedDict([('A', '1'), + ('B', '2'), + ('C', '3')]), + definition.entries) + + def testEnumValueAssignmentReferences(self): + definition = EnumDefinition(original_enum_name='c', enum_package='p') + definition.AppendEntry('A', None) + definition.AppendEntry('B', 'A') + definition.AppendEntry('C', None) + definition.AppendEntry('D', 'C') + definition.Finalize() + self.assertEqual(collections.OrderedDict([('A', 0), + ('B', 0), + ('C', 1), + ('D', 1)]), + definition.entries) + + def testEnumValueAssignmentSet(self): + definition = EnumDefinition(original_enum_name='c', enum_package='p') + definition.AppendEntry('A', None) + definition.AppendEntry('B', '2') + definition.AppendEntry('C', None) + definition.Finalize() + self.assertEqual(collections.OrderedDict([('A', 0), + ('B', 2), + ('C', 3)]), + definition.entries) + + def testEnumValueAssignmentSetReferences(self): + definition = EnumDefinition(original_enum_name='c', enum_package='p') + definition.AppendEntry('A', None) + definition.AppendEntry('B', 'A') + definition.AppendEntry('C', 'B') + definition.AppendEntry('D', None) + definition.Finalize() + self.assertEqual(collections.OrderedDict([('A', 0), + ('B', 0), + ('C', 0), + ('D', 1)]), + definition.entries) + + def testEnumValueAssignmentRaises(self): + definition = EnumDefinition(original_enum_name='c', enum_package='p') + definition.AppendEntry('A', None) + definition.AppendEntry('B', 'foo') + definition.AppendEntry('C', None) + with self.assertRaises(Exception): + definition.Finalize() + + def testExplicitPrefixStripping(self): + definition = EnumDefinition(original_enum_name='c', enum_package='p') + definition.AppendEntry('P_A', None) + definition.AppendEntry('B', None) + definition.AppendEntry('P_C', None) + definition.AppendEntry('P_LAST', 'P_C') + definition.prefix_to_strip = 'P_' + definition.Finalize() + self.assertEqual(collections.OrderedDict([('A', 0), + ('B', 1), + ('C', 2), + ('LAST', 2)]), + definition.entries) + + def testImplicitPrefixStripping(self): + definition = EnumDefinition(original_enum_name='ClassName', + enum_package='p') + definition.AppendEntry('CLASS_NAME_A', None) + definition.AppendEntry('CLASS_NAME_B', None) + definition.AppendEntry('CLASS_NAME_C', None) + definition.AppendEntry('CLASS_NAME_LAST', 'CLASS_NAME_C') + definition.Finalize() + self.assertEqual(collections.OrderedDict([('A', 0), + ('B', 1), + ('C', 2), + ('LAST', 2)]), + definition.entries) + + def testImplicitPrefixStrippingRequiresAllConstantsToBePrefixed(self): + definition = EnumDefinition(original_enum_name='Name', + enum_package='p') + definition.AppendEntry('A', None) + definition.AppendEntry('B', None) + definition.AppendEntry('NAME_LAST', None) + definition.Finalize() + self.assertEqual(['A', 'B', 'NAME_LAST'], definition.entries.keys()) + + def testGenerateThrowsOnEmptyInput(self): + with self.assertRaises(Exception): + original_do_parse = java_cpp_enum.DoParseHeaderFile + try: + java_cpp_enum.DoParseHeaderFile = lambda _: [] + java_cpp_enum.DoGenerate('dir', ['file']) + finally: + java_cpp_enum.DoParseHeaderFile = original_do_parse + +def main(argv): + parser = optparse.OptionParser() + parser.add_option("--stamp", help="File to touch on success.") + options, _ = parser.parse_args(argv) + + suite = unittest.TestLoader().loadTestsFromTestCase(TestPreprocess) + unittest.TextTestRunner(verbosity=0).run(suite) + + if options.stamp: + build_utils.Touch(options.stamp) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/engine/src/flutter/build/android/gyp/javac.py b/engine/src/flutter/build/android/gyp/javac.py new file mode 100755 index 0000000000..e36fd43bc0 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/javac.py @@ -0,0 +1,282 @@ +#!/usr/bin/env python +# +# Copyright 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. + +import fnmatch +import optparse +import os +import shutil +import re +import sys +import textwrap + +from util import build_utils +from util import md5_check + +import jar + +sys.path.append(build_utils.COLORAMA_ROOT) +import colorama + + +def ColorJavacOutput(output): + fileline_prefix = r'(?P(?P[-.\w/\\]+.java):(?P[0-9]+):)' + warning_re = re.compile( + fileline_prefix + r'(?P warning: (?P.*))$') + error_re = re.compile( + fileline_prefix + r'(?P (?P.*))$') + marker_re = re.compile(r'\s*(?P\^)\s*$') + + warning_color = ['full_message', colorama.Fore.YELLOW + colorama.Style.DIM] + error_color = ['full_message', colorama.Fore.MAGENTA + colorama.Style.BRIGHT] + marker_color = ['marker', colorama.Fore.BLUE + colorama.Style.BRIGHT] + + def Colorize(line, regex, color): + match = regex.match(line) + start = match.start(color[0]) + end = match.end(color[0]) + return (line[:start] + + color[1] + line[start:end] + + colorama.Fore.RESET + colorama.Style.RESET_ALL + + line[end:]) + + def ApplyColor(line): + if warning_re.match(line): + line = Colorize(line, warning_re, warning_color) + elif error_re.match(line): + line = Colorize(line, error_re, error_color) + elif marker_re.match(line): + line = Colorize(line, marker_re, marker_color) + return line + + return '\n'.join(map(ApplyColor, output.split('\n'))) + + +def DoJavac( + classpath, classes_dir, chromium_code, java_files): + """Runs javac. + + Builds |java_files| with the provided |classpath| and puts the generated + .class files into |classes_dir|. If |chromium_code| is true, extra lint + checking will be enabled. + """ + + jar_inputs = [] + for path in classpath: + if os.path.exists(path + '.TOC'): + jar_inputs.append(path + '.TOC') + else: + jar_inputs.append(path) + + javac_args = [ + '-g', + # Chromium only allows UTF8 source files. Being explicit avoids + # javac pulling a default encoding from the user's environment. + '-encoding', 'UTF-8', + '-source', '1.7', + '-target', '1.7', + '-classpath', ':'.join(classpath), + '-d', classes_dir] + if chromium_code: + # TODO(aurimas): re-enable '-Xlint:deprecation' checks once they are fixed. + javac_args.extend(['-Xlint:unchecked']) + else: + # XDignore.symbol.file makes javac compile against rt.jar instead of + # ct.sym. This means that using a java internal package/class will not + # trigger a compile warning or error. + javac_args.extend(['-XDignore.symbol.file']) + + javac_cmd = ['javac'] + javac_args + java_files + + def Compile(): + build_utils.CheckOutput( + javac_cmd, + print_stdout=chromium_code, + stderr_filter=ColorJavacOutput) + + record_path = os.path.join(classes_dir, 'javac.md5.stamp') + md5_check.CallAndRecordIfStale( + Compile, + record_path=record_path, + input_paths=java_files + jar_inputs, + input_strings=javac_cmd) + + +_MAX_MANIFEST_LINE_LEN = 72 + + +def CreateManifest(manifest_path, classpath, main_class=None, + manifest_entries=None): + """Creates a manifest file with the given parameters. + + This generates a manifest file that compiles with the spec found at + http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html#JAR_Manifest + + Args: + manifest_path: The path to the manifest file that should be created. + classpath: The JAR files that should be listed on the manifest file's + classpath. + main_class: If present, the class containing the main() function. + manifest_entries: If present, a list of (key, value) pairs to add to + the manifest. + + """ + output = ['Manifest-Version: 1.0'] + if main_class: + output.append('Main-Class: %s' % main_class) + if manifest_entries: + for k, v in manifest_entries: + output.append('%s: %s' % (k, v)) + if classpath: + sanitized_paths = [] + for path in classpath: + sanitized_paths.append(os.path.basename(path.strip('"'))) + output.append('Class-Path: %s' % ' '.join(sanitized_paths)) + output.append('Created-By: ') + output.append('') + + wrapper = textwrap.TextWrapper(break_long_words=True, + drop_whitespace=False, + subsequent_indent=' ', + width=_MAX_MANIFEST_LINE_LEN - 2) + output = '\r\n'.join(w for l in output for w in wrapper.wrap(l)) + + with open(manifest_path, 'w') as f: + f.write(output) + + +def main(argv): + colorama.init() + + argv = build_utils.ExpandFileArgs(argv) + + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option( + '--src-gendirs', + help='Directories containing generated java files.') + parser.add_option( + '--java-srcjars', + action='append', + default=[], + help='List of srcjars to include in compilation.') + parser.add_option( + '--classpath', + action='append', + help='Classpath for javac. If this is specified multiple times, they ' + 'will all be appended to construct the classpath.') + parser.add_option( + '--javac-includes', + help='A list of file patterns. If provided, only java files that match' + 'one of the patterns will be compiled.') + parser.add_option( + '--jar-excluded-classes', + default='', + help='List of .class file patterns to exclude from the jar.') + + parser.add_option( + '--chromium-code', + type='int', + help='Whether code being compiled should be built with stricter ' + 'warnings for chromium code.') + + parser.add_option( + '--classes-dir', + help='Directory for compiled .class files.') + parser.add_option('--jar-path', help='Jar output path.') + parser.add_option( + '--main-class', + help='The class containing the main method.') + parser.add_option( + '--manifest-entry', + action='append', + help='Key:value pairs to add to the .jar manifest.') + + parser.add_option('--stamp', help='Path to touch on success.') + + options, args = parser.parse_args(argv) + + if options.main_class and not options.jar_path: + parser.error('--main-class requires --jar-path') + + classpath = [] + for arg in options.classpath: + classpath += build_utils.ParseGypList(arg) + + java_srcjars = [] + for arg in options.java_srcjars: + java_srcjars += build_utils.ParseGypList(arg) + + java_files = args + if options.src_gendirs: + src_gendirs = build_utils.ParseGypList(options.src_gendirs) + java_files += build_utils.FindInDirectories(src_gendirs, '*.java') + + input_files = classpath + java_srcjars + java_files + with build_utils.TempDir() as temp_dir: + classes_dir = os.path.join(temp_dir, 'classes') + os.makedirs(classes_dir) + if java_srcjars: + java_dir = os.path.join(temp_dir, 'java') + os.makedirs(java_dir) + for srcjar in java_srcjars: + build_utils.ExtractAll(srcjar, path=java_dir, pattern='*.java') + java_files += build_utils.FindInDirectory(java_dir, '*.java') + + if options.javac_includes: + javac_includes = build_utils.ParseGypList(options.javac_includes) + filtered_java_files = [] + for f in java_files: + for include in javac_includes: + if fnmatch.fnmatch(f, include): + filtered_java_files.append(f) + break + java_files = filtered_java_files + + if len(java_files) != 0: + DoJavac( + classpath, + classes_dir, + options.chromium_code, + java_files) + + if options.jar_path: + if options.main_class or options.manifest_entry: + if options.manifest_entry: + entries = map(lambda e: e.split(":"), options.manifest_entry) + else: + entries = [] + manifest_file = os.path.join(temp_dir, 'manifest') + CreateManifest(manifest_file, classpath, options.main_class, entries) + else: + manifest_file = None + jar.JarDirectory(classes_dir, + build_utils.ParseGypList(options.jar_excluded_classes), + options.jar_path, + manifest_file=manifest_file) + + if options.classes_dir: + # Delete the old classes directory. This ensures that all .class files in + # the output are actually from the input .java files. For example, if a + # .java file is deleted or an inner class is removed, the classes + # directory should not contain the corresponding old .class file after + # running this action. + build_utils.DeleteDirectory(options.classes_dir) + shutil.copytree(classes_dir, options.classes_dir) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + input_files + build_utils.GetPythonDependencies()) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) + + diff --git a/engine/src/flutter/build/android/gyp/jinja_template.py b/engine/src/flutter/build/android/gyp/jinja_template.py new file mode 100755 index 0000000000..e7c9a3436a --- /dev/null +++ b/engine/src/flutter/build/android/gyp/jinja_template.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Renders one or more template files using the Jinja template engine.""" + +import codecs +import optparse +import os +import sys + +from util import build_utils + +# Import jinja2 from third_party/jinja2 +sys.path.append(os.path.join(os.path.dirname(__file__), '../../../third_party')) +import jinja2 # pylint: disable=F0401 + + +class RecordingFileSystemLoader(jinja2.FileSystemLoader): + '''A FileSystemLoader that stores a list of loaded templates.''' + def __init__(self, searchpath): + jinja2.FileSystemLoader.__init__(self, searchpath) + self.loaded_templates = set() + + def get_source(self, environment, template): + contents, filename, uptodate = jinja2.FileSystemLoader.get_source( + self, environment, template) + self.loaded_templates.add(os.path.relpath(filename)) + return contents, filename, uptodate + + def get_loaded_templates(self): + return list(self.loaded_templates) + + +def ProcessFile(env, input_filename, loader_base_dir, output_filename, + variables): + input_rel_path = os.path.relpath(input_filename, loader_base_dir) + template = env.get_template(input_rel_path) + output = template.render(variables) + with codecs.open(output_filename, 'w', 'utf-8') as output_file: + output_file.write(output) + + +def ProcessFiles(env, input_filenames, loader_base_dir, inputs_base_dir, + outputs_zip, variables): + with build_utils.TempDir() as temp_dir: + for input_filename in input_filenames: + relpath = os.path.relpath(os.path.abspath(input_filename), + os.path.abspath(inputs_base_dir)) + if relpath.startswith(os.pardir): + raise Exception('input file %s is not contained in inputs base dir %s' + % (input_filename, inputs_base_dir)) + + output_filename = os.path.join(temp_dir, relpath) + parent_dir = os.path.dirname(output_filename) + build_utils.MakeDirectory(parent_dir) + ProcessFile(env, input_filename, loader_base_dir, output_filename, + variables) + + build_utils.ZipDir(outputs_zip, temp_dir) + + +def main(): + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option('--inputs', help='The template files to process.') + parser.add_option('--output', help='The output file to generate. Valid ' + 'only if there is a single input.') + parser.add_option('--outputs-zip', help='A zip file containing the processed ' + 'templates. Required if there are multiple inputs.') + parser.add_option('--inputs-base-dir', help='A common ancestor directory of ' + 'the inputs. Each output\'s path in the output zip will ' + 'match the relative path from INPUTS_BASE_DIR to the ' + 'input. Required if --output-zip is given.') + parser.add_option('--loader-base-dir', help='Base path used by the template ' + 'loader. Must be a common ancestor directory of ' + 'the inputs. Defaults to CHROMIUM_SRC.', + default=build_utils.CHROMIUM_SRC) + parser.add_option('--variables', help='Variables to be made available in the ' + 'template processing environment, as a GYP list (e.g. ' + '--variables "channel=beta mstone=39")', default='') + options, args = parser.parse_args() + + build_utils.CheckOptions(options, parser, required=['inputs']) + inputs = build_utils.ParseGypList(options.inputs) + + if (options.output is None) == (options.outputs_zip is None): + parser.error('Exactly one of --output and --output-zip must be given') + if options.output and len(inputs) != 1: + parser.error('--output cannot be used with multiple inputs') + if options.outputs_zip and not options.inputs_base_dir: + parser.error('--inputs-base-dir must be given when --output-zip is used') + if args: + parser.error('No positional arguments should be given.') + + variables = {} + for v in build_utils.ParseGypList(options.variables): + if '=' not in v: + parser.error('--variables argument must contain "=": ' + v) + name, _, value = v.partition('=') + variables[name] = value + + loader = RecordingFileSystemLoader(options.loader_base_dir) + env = jinja2.Environment(loader=loader, undefined=jinja2.StrictUndefined, + line_comment_prefix='##') + if options.output: + ProcessFile(env, inputs[0], options.loader_base_dir, options.output, + variables) + else: + ProcessFiles(env, inputs, options.loader_base_dir, options.inputs_base_dir, + options.outputs_zip, variables) + + if options.depfile: + deps = loader.get_loaded_templates() + build_utils.GetPythonDependencies() + build_utils.WriteDepfile(options.depfile, deps) + + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/build/android/gyp/lint.py b/engine/src/flutter/build/android/gyp/lint.py new file mode 100755 index 0000000000..9402780b09 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/lint.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# +# 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. + +"""Runs Android's lint tool.""" + + +import optparse +import os +import sys +from xml.dom import minidom + +from util import build_utils + + +_SRC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), + '..', '..', '..')) + + +def _RunLint(lint_path, config_path, processed_config_path, manifest_path, + result_path, product_dir, sources, jar_path, resource_dir=None, + can_fail_build=False): + + def _RelativizePath(path): + """Returns relative path to top-level src dir. + + Args: + path: A path relative to cwd. + """ + return os.path.relpath(os.path.abspath(path), _SRC_ROOT) + + def _ProcessConfigFile(): + if not build_utils.IsTimeStale(processed_config_path, [config_path]): + return + + with open(config_path, 'rb') as f: + content = f.read().replace( + 'PRODUCT_DIR', _RelativizePath(product_dir)) + + with open(processed_config_path, 'wb') as f: + f.write(content) + + def _ProcessResultFile(): + with open(result_path, 'rb') as f: + content = f.read().replace( + _RelativizePath(product_dir), 'PRODUCT_DIR') + + with open(result_path, 'wb') as f: + f.write(content) + + def _ParseAndShowResultFile(): + dom = minidom.parse(result_path) + issues = dom.getElementsByTagName('issue') + print >> sys.stderr + for issue in issues: + issue_id = issue.attributes['id'].value + message = issue.attributes['message'].value + location_elem = issue.getElementsByTagName('location')[0] + path = location_elem.attributes['file'].value + line = location_elem.getAttribute('line') + if line: + error = '%s:%s %s: %s [warning]' % (path, line, message, issue_id) + else: + # Issues in class files don't have a line number. + error = '%s %s: %s [warning]' % (path, message, issue_id) + print >> sys.stderr, error + for attr in ['errorLine1', 'errorLine2']: + error_line = issue.getAttribute(attr) + if error_line: + print >> sys.stderr, error_line + return len(issues) + + with build_utils.TempDir() as temp_dir: + _ProcessConfigFile() + + cmd = [ + _RelativizePath(lint_path), '-Werror', '--exitcode', '--showall', + '--config', _RelativizePath(processed_config_path), + '--classpath', _RelativizePath(jar_path), + '--xml', _RelativizePath(result_path), + ] + if resource_dir: + cmd.extend(['--resources', _RelativizePath(resource_dir)]) + + # There may be multiple source files with the same basename (but in + # different directories). It is difficult to determine what part of the path + # corresponds to the java package, and so instead just link the source files + # into temporary directories (creating a new one whenever there is a name + # conflict). + src_dirs = [] + def NewSourceDir(): + new_dir = os.path.join(temp_dir, str(len(src_dirs))) + os.mkdir(new_dir) + src_dirs.append(new_dir) + cmd.extend(['--sources', _RelativizePath(new_dir)]) + return new_dir + + def PathInDir(d, src): + return os.path.join(d, os.path.basename(src)) + + for src in sources: + src_dir = None + for d in src_dirs: + if not os.path.exists(PathInDir(d, src)): + src_dir = d + break + if not src_dir: + src_dir = NewSourceDir() + os.symlink(os.path.abspath(src), PathInDir(src_dir, src)) + + cmd.append(_RelativizePath(os.path.join(manifest_path, os.pardir))) + + if os.path.exists(result_path): + os.remove(result_path) + + try: + build_utils.CheckOutput(cmd, cwd=_SRC_ROOT) + except build_utils.CalledProcessError as e: + # There is a problem with lint usage + if not os.path.exists(result_path): + print 'Something is wrong:' + print e + return 0 + + # There are actual lint issues + else: + try: + num_issues = _ParseAndShowResultFile() + except Exception: + print 'Lint created unparseable xml file...' + print 'File contents:' + with open(result_path) as f: + print f.read() + return 0 + + _ProcessResultFile() + msg = ('\nLint found %d new issues.\n' + ' - For full explanation refer to %s\n' + ' - Wanna suppress these issues?\n' + ' 1. Read comment in %s\n' + ' 2. Run "python %s %s"\n' % + (num_issues, + _RelativizePath(result_path), + _RelativizePath(config_path), + _RelativizePath(os.path.join(_SRC_ROOT, 'build', 'android', + 'lint', 'suppress.py')), + _RelativizePath(result_path))) + print >> sys.stderr, msg + return 1 if can_fail_build else 0 + + return 0 + + +def main(): + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option('--lint-path', help='Path to lint executable.') + parser.add_option('--config-path', help='Path to lint suppressions file.') + parser.add_option('--processed-config-path', + help='Path to processed lint suppressions file.') + parser.add_option('--manifest-path', help='Path to AndroidManifest.xml') + parser.add_option('--result-path', help='Path to XML lint result file.') + parser.add_option('--product-dir', help='Path to product dir.') + parser.add_option('--src-dirs', help='Directories containing java files.') + parser.add_option('--java-files', help='Paths to java files.') + parser.add_option('--jar-path', help='Jar file containing class files.') + parser.add_option('--resource-dir', help='Path to resource dir.') + parser.add_option('--can-fail-build', action='store_true', + help='If set, script will exit with nonzero exit status' + ' if lint errors are present') + parser.add_option('--stamp', help='Path to touch on success.') + parser.add_option('--enable', action='store_true', + help='Run lint instead of just touching stamp.') + + options, _ = parser.parse_args() + + build_utils.CheckOptions( + options, parser, required=['lint_path', 'config_path', + 'processed_config_path', 'manifest_path', + 'result_path', 'product_dir', + 'jar_path']) + + rc = 0 + + if options.enable: + sources = [] + if options.src_dirs: + src_dirs = build_utils.ParseGypList(options.src_dirs) + sources = build_utils.FindInDirectories(src_dirs, '*.java') + elif options.java_files: + sources = build_utils.ParseGypList(options.java_files) + else: + print 'One of --src-dirs or --java-files must be specified.' + return 1 + rc = _RunLint(options.lint_path, options.config_path, + options.processed_config_path, + options.manifest_path, options.result_path, + options.product_dir, sources, options.jar_path, + options.resource_dir, options.can_fail_build) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + build_utils.GetPythonDependencies()) + + if options.stamp and not rc: + build_utils.Touch(options.stamp) + + return rc + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/gyp/pack_arm_relocations.py b/engine/src/flutter/build/android/gyp/pack_arm_relocations.py new file mode 100755 index 0000000000..02e4499993 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/pack_arm_relocations.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Pack relocations in a library (or copy unchanged). + +If --enable-packing and --configuration-name=='Release', invoke the +relocation_packer tool to pack the .rel.dyn or .rela.dyn section in the given +library files. This step is inserted after the libraries are stripped. + +If --enable-packing is zero, the script copies files verbatim, with no +attempt to pack relocations. + +Any library listed in --exclude-packing-list is also copied verbatim, +irrespective of any --enable-packing setting. Typically this would be +'libchromium_android_linker.so'. +""" + +import optparse +import os +import shlex +import shutil +import sys +import tempfile + +from util import build_utils + +def PackLibraryRelocations(android_pack_relocations, library_path, output_path): + shutil.copy(library_path, output_path) + pack_command = [android_pack_relocations, output_path] + build_utils.CheckOutput(pack_command) + + +def CopyLibraryUnchanged(library_path, output_path): + shutil.copy(library_path, output_path) + + +def main(args): + args = build_utils.ExpandFileArgs(args) + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option('--clear-dir', action='store_true', + help='If set, the destination directory will be deleted ' + 'before copying files to it. This is highly recommended to ' + 'ensure that no stale files are left in the directory.') + + parser.add_option('--configuration-name', + default='Release', + help='Gyp configuration name (i.e. Debug, Release)') + parser.add_option('--enable-packing', + choices=['0', '1'], + help=('Pack relocations if 1 and configuration name is \'Release\',' + ' otherwise plain file copy')) + parser.add_option('--exclude-packing-list', + default='', + help='Names of any libraries explicitly not packed') + parser.add_option('--android-pack-relocations', + help='Path to the relocations packer binary') + parser.add_option('--stripped-libraries-dir', + help='Directory for stripped libraries') + parser.add_option('--packed-libraries-dir', + help='Directory for packed libraries') + parser.add_option('--libraries', action='append', + help='List of libraries') + parser.add_option('--stamp', help='Path to touch on success') + + options, _ = parser.parse_args(args) + enable_packing = (options.enable_packing == '1' and + options.configuration_name == 'Release') + exclude_packing_set = set(shlex.split(options.exclude_packing_list)) + + libraries = [] + for libs_arg in options.libraries: + libraries += build_utils.ParseGypList(libs_arg) + + if options.clear_dir: + build_utils.DeleteDirectory(options.packed_libraries_dir) + + build_utils.MakeDirectory(options.packed_libraries_dir) + + for library in libraries: + library_path = os.path.join(options.stripped_libraries_dir, library) + output_path = os.path.join( + options.packed_libraries_dir, os.path.basename(library)) + + if enable_packing and library not in exclude_packing_set: + PackLibraryRelocations(options.android_pack_relocations, + library_path, + output_path) + else: + CopyLibraryUnchanged(library_path, output_path) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + libraries + build_utils.GetPythonDependencies()) + + if options.stamp: + build_utils.Touch(options.stamp) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/package_resources.py b/engine/src/flutter/build/android/gyp/package_resources.py new file mode 100755 index 0000000000..f2c2a6ed9e --- /dev/null +++ b/engine/src/flutter/build/android/gyp/package_resources.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +# pylint: disable=C0301 +"""Package resources into an apk. + +See https://android.googlesource.com/platform/tools/base/+/master/legacy/ant-tasks/src/main/java/com/android/ant/AaptExecTask.java +and +https://android.googlesource.com/platform/sdk/+/master/files/ant/build.xml +""" +# pylint: enable=C0301 + +import optparse +import os +import shutil +import zipfile + +from util import build_utils + + +# List is generated from the chrome_apk.apk_intermediates.ap_ via: +# unzip -l $FILE_AP_ | cut -c31- | grep res/draw | cut -d'/' -f 2 | sort \ +# | uniq | grep -- -tvdpi- | cut -c10- +# and then manually sorted. +# Note that we can't just do a cross-product of dimentions because the filenames +# become too big and aapt fails to create the files. +# This leaves all default drawables (mdpi) in the main apk. Android gets upset +# though if any drawables are missing from the default drawables/ directory. +DENSITY_SPLITS = { + 'hdpi': ( + 'hdpi-v4', # Order matters for output file names. + 'ldrtl-hdpi-v4', + 'sw600dp-hdpi-v13', + 'ldrtl-hdpi-v17', + 'ldrtl-sw600dp-hdpi-v17', + 'hdpi-v21', + ), + 'xhdpi': ( + 'xhdpi-v4', + 'ldrtl-xhdpi-v4', + 'sw600dp-xhdpi-v13', + 'ldrtl-xhdpi-v17', + 'ldrtl-sw600dp-xhdpi-v17', + 'xhdpi-v21', + ), + 'xxhdpi': ( + 'xxhdpi-v4', + 'ldrtl-xxhdpi-v4', + 'sw600dp-xxhdpi-v13', + 'ldrtl-xxhdpi-v17', + 'ldrtl-sw600dp-xxhdpi-v17', + 'xxhdpi-v21', + ), + 'tvdpi': ( + 'tvdpi-v4', + 'sw600dp-tvdpi-v13', + 'ldrtl-sw600dp-tvdpi-v17', + ), +} + + +def ParseArgs(): + """Parses command line options. + + Returns: + An options object as from optparse.OptionsParser.parse_args() + """ + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option('--android-sdk', help='path to the Android SDK folder') + parser.add_option('--aapt-path', + help='path to the Android aapt tool') + + parser.add_option('--configuration-name', + help='Gyp\'s configuration name (Debug or Release).') + + parser.add_option('--android-manifest', help='AndroidManifest.xml path') + parser.add_option('--version-code', help='Version code for apk.') + parser.add_option('--version-name', help='Version name for apk.') + parser.add_option( + '--shared-resources', + action='store_true', + help='Make a resource package that can be loaded by a different' + 'application at runtime to access the package\'s resources.') + parser.add_option('--resource-zips', + help='zip files containing resources to be packaged') + parser.add_option('--asset-dir', + help='directories containing assets to be packaged') + parser.add_option('--no-compress', help='disables compression for the ' + 'given comma separated list of extensions') + parser.add_option( + '--create-density-splits', + action='store_true', + help='Enables density splits') + + parser.add_option('--apk-path', + help='Path to output (partial) apk.') + + (options, args) = parser.parse_args() + + if args: + parser.error('No positional arguments should be given.') + + # Check that required options have been provided. + required_options = ('android_sdk', 'aapt_path', 'configuration_name', + 'android_manifest', 'version_code', 'version_name', + 'apk_path') + + build_utils.CheckOptions(options, parser, required=required_options) + + return options + + +def MoveImagesToNonMdpiFolders(res_root): + """Move images from drawable-*-mdpi-* folders to drawable-* folders. + + Why? http://crbug.com/289843 + """ + for src_dir_name in os.listdir(res_root): + src_components = src_dir_name.split('-') + if src_components[0] != 'drawable' or 'mdpi' not in src_components: + continue + src_dir = os.path.join(res_root, src_dir_name) + if not os.path.isdir(src_dir): + continue + dst_components = [c for c in src_components if c != 'mdpi'] + assert dst_components != src_components + dst_dir_name = '-'.join(dst_components) + dst_dir = os.path.join(res_root, dst_dir_name) + build_utils.MakeDirectory(dst_dir) + for src_file_name in os.listdir(src_dir): + if not src_file_name.endswith('.png'): + continue + src_file = os.path.join(src_dir, src_file_name) + dst_file = os.path.join(dst_dir, src_file_name) + assert not os.path.lexists(dst_file) + shutil.move(src_file, dst_file) + + +def PackageArgsForExtractedZip(d): + """Returns the aapt args for an extracted resources zip. + + A resources zip either contains the resources for a single target or for + multiple targets. If it is multiple targets merged into one, the actual + resource directories will be contained in the subdirectories 0, 1, 2, ... + """ + subdirs = [os.path.join(d, s) for s in os.listdir(d)] + subdirs = [s for s in subdirs if os.path.isdir(s)] + is_multi = '0' in [os.path.basename(s) for s in subdirs] + if is_multi: + res_dirs = sorted(subdirs, key=lambda p : int(os.path.basename(p))) + else: + res_dirs = [d] + package_command = [] + for d in res_dirs: + MoveImagesToNonMdpiFolders(d) + package_command += ['-S', d] + return package_command + + +def RenameDensitySplits(apk_path): + """Renames all density splits to have shorter / predictable names.""" + for density, config in DENSITY_SPLITS.iteritems(): + src_path = '%s_%s' % (apk_path, '_'.join(config)) + dst_path = '%s-%s' % (apk_path, density) + if os.path.exists(dst_path): + os.unlink(dst_path) + os.rename(src_path, dst_path) + + +def CheckDensityMissedConfigs(apk_path): + """Raises an exception if apk_path contains any density-specifc files.""" + triggers = ['-%s' % density for density in DENSITY_SPLITS] + with zipfile.ZipFile(apk_path) as main_apk_zip: + for name in main_apk_zip.namelist(): + for trigger in triggers: + if trigger in name and not 'mipmap-' in name: + raise Exception(('Found density in main apk that should have been ' + + 'put into a split: %s\nYou need to update ' + + 'package_resources.py to include this new ' + + 'config.') % name) + + +def main(): + options = ParseArgs() + android_jar = os.path.join(options.android_sdk, 'android.jar') + aapt = options.aapt_path + + with build_utils.TempDir() as temp_dir: + package_command = [aapt, + 'package', + '--version-code', options.version_code, + '--version-name', options.version_name, + '-M', options.android_manifest, + '--no-crunch', + '-f', + '--auto-add-overlay', + '-I', android_jar, + '-F', options.apk_path, + '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN, + ] + + if options.no_compress: + for ext in options.no_compress.split(','): + package_command += ['-0', ext] + if options.shared_resources: + package_command.append('--shared-lib') + + if options.asset_dir and os.path.exists(options.asset_dir): + package_command += ['-A', options.asset_dir] + + if options.resource_zips: + dep_zips = build_utils.ParseGypList(options.resource_zips) + for z in dep_zips: + subdir = os.path.join(temp_dir, os.path.basename(z)) + if os.path.exists(subdir): + raise Exception('Resource zip name conflict: ' + os.path.basename(z)) + build_utils.ExtractAll(z, path=subdir) + package_command += PackageArgsForExtractedZip(subdir) + + if options.create_density_splits: + for config in DENSITY_SPLITS.itervalues(): + package_command.extend(('--split', ','.join(config))) + + if 'Debug' in options.configuration_name: + package_command += ['--debug-mode'] + + build_utils.CheckOutput( + package_command, print_stdout=False, print_stderr=False) + + if options.create_density_splits: + CheckDensityMissedConfigs(options.apk_path) + RenameDensitySplits(options.apk_path) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + build_utils.GetPythonDependencies()) + + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/build/android/gyp/process_resources.py b/engine/src/flutter/build/android/gyp/process_resources.py new file mode 100755 index 0000000000..d227954ae9 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/process_resources.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 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. + +"""Process Android resources to generate R.java, and prepare for packaging. + +This will crunch images and generate v14 compatible resources +(see generate_v14_compatible_resources.py). +""" + +import codecs +import optparse +import os +import re +import shutil +import sys +import zipfile + +import generate_v14_compatible_resources + +from util import build_utils + +# Import jinja2 from third_party/jinja2 +sys.path.insert(1, + os.path.join(os.path.dirname(__file__), '../../../third_party')) +from jinja2 import Template # pylint: disable=F0401 + + +def ParseArgs(args): + """Parses command line options. + + Returns: + An options object as from optparse.OptionsParser.parse_args() + """ + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option('--android-sdk', help='path to the Android SDK folder') + parser.add_option('--aapt-path', + help='path to the Android aapt tool') + parser.add_option('--non-constant-id', action='store_true') + + parser.add_option('--android-manifest', help='AndroidManifest.xml path') + parser.add_option('--custom-package', help='Java package for R.java') + parser.add_option( + '--shared-resources', + action='store_true', + help='Make a resource package that can be loaded by a different' + 'application at runtime to access the package\'s resources.') + + parser.add_option('--resource-dirs', + help='Directories containing resources of this target.') + parser.add_option('--dependencies-res-zips', + help='Resources from dependents.') + + parser.add_option('--resource-zip-out', + help='Path for output zipped resources.') + + parser.add_option('--R-dir', + help='directory to hold generated R.java.') + parser.add_option('--srcjar-out', + help='Path to srcjar to contain generated R.java.') + parser.add_option('--r-text-out', + help='Path to store the R.txt file generated by appt.') + + parser.add_option('--proguard-file', + help='Path to proguard.txt generated file') + + parser.add_option( + '--v14-skip', + action="store_true", + help='Do not generate nor verify v14 resources') + + parser.add_option( + '--extra-res-packages', + help='Additional package names to generate R.java files for') + parser.add_option( + '--extra-r-text-files', + help='For each additional package, the R.txt file should contain a ' + 'list of resources to be included in the R.java file in the format ' + 'generated by aapt') + parser.add_option( + '--include-all-resources', + action='store_true', + help='Include every resource ID in every generated R.java file ' + '(ignoring R.txt).') + + parser.add_option( + '--all-resources-zip-out', + help='Path for output of all resources. This includes resources in ' + 'dependencies.') + + parser.add_option('--stamp', help='File to touch on success') + + (options, args) = parser.parse_args(args) + + if args: + parser.error('No positional arguments should be given.') + + # Check that required options have been provided. + required_options = ( + 'android_sdk', + 'aapt_path', + 'android_manifest', + 'dependencies_res_zips', + 'resource_dirs', + 'resource_zip_out', + ) + build_utils.CheckOptions(options, parser, required=required_options) + + if (options.R_dir is None) == (options.srcjar_out is None): + raise Exception('Exactly one of --R-dir or --srcjar-out must be specified.') + + return options + + +def CreateExtraRJavaFiles( + r_dir, extra_packages, extra_r_text_files, shared_resources, include_all): + if include_all: + java_files = build_utils.FindInDirectory(r_dir, "R.java") + if len(java_files) != 1: + return + r_java_file = java_files[0] + r_java_contents = codecs.open(r_java_file, encoding='utf-8').read() + + for package in extra_packages: + package_r_java_dir = os.path.join(r_dir, *package.split('.')) + build_utils.MakeDirectory(package_r_java_dir) + package_r_java_path = os.path.join(package_r_java_dir, 'R.java') + new_r_java = re.sub(r'package [.\w]*;', u'package %s;' % package, + r_java_contents) + codecs.open(package_r_java_path, 'w', encoding='utf-8').write(new_r_java) + else: + if len(extra_packages) != len(extra_r_text_files): + raise Exception('Need one R.txt file per extra package') + + all_resources = {} + r_txt_file = os.path.join(r_dir, 'R.txt') + if not os.path.exists(r_txt_file): + return + with open(r_txt_file) as f: + for line in f: + m = re.match(r'(int(?:\[\])?) (\w+) (\w+) (.+)$', line) + if not m: + raise Exception('Unexpected line in R.txt: %s' % line) + java_type, resource_type, name, value = m.groups() + all_resources[(resource_type, name)] = (java_type, value) + + for package, r_text_file in zip(extra_packages, extra_r_text_files): + if os.path.exists(r_text_file): + package_r_java_dir = os.path.join(r_dir, *package.split('.')) + build_utils.MakeDirectory(package_r_java_dir) + package_r_java_path = os.path.join(package_r_java_dir, 'R.java') + CreateExtraRJavaFile( + package, package_r_java_path, r_text_file, all_resources, + shared_resources) + + +def CreateExtraRJavaFile( + package, r_java_path, r_text_file, all_resources, shared_resources): + resources = {} + with open(r_text_file) as f: + for line in f: + m = re.match(r'int(?:\[\])? (\w+) (\w+) ', line) + if not m: + raise Exception('Unexpected line in R.txt: %s' % line) + resource_type, name = m.groups() + java_type, value = all_resources[(resource_type, name)] + if resource_type not in resources: + resources[resource_type] = [] + resources[resource_type].append((name, java_type, value)) + + template = Template("""/* AUTO-GENERATED FILE. DO NOT MODIFY. */ + +package {{ package }}; + +public final class R { + {% for resource_type in resources %} + public static final class {{ resource_type }} { + {% for name, java_type, value in resources[resource_type] %} + {% if shared_resources %} + public static {{ java_type }} {{ name }} = {{ value }}; + {% else %} + public static final {{ java_type }} {{ name }} = {{ value }}; + {% endif %} + {% endfor %} + } + {% endfor %} + {% if shared_resources %} + public static void onResourcesLoaded(int packageId) { + {% for resource_type in resources %} + {% for name, java_type, value in resources[resource_type] %} + {% if java_type == 'int[]' %} + for(int i = 0; i < {{ resource_type }}.{{ name }}.length; ++i) { + {{ resource_type }}.{{ name }}[i] = + ({{ resource_type }}.{{ name }}[i] & 0x00ffffff) + | (packageId << 24); + } + {% else %} + {{ resource_type }}.{{ name }} = + ({{ resource_type }}.{{ name }} & 0x00ffffff) + | (packageId << 24); + {% endif %} + {% endfor %} + {% endfor %} + } + {% endif %} +} +""", trim_blocks=True, lstrip_blocks=True) + + output = template.render(package=package, resources=resources, + shared_resources=shared_resources) + with open(r_java_path, 'w') as f: + f.write(output) + + +def CrunchDirectory(aapt, input_dir, output_dir): + """Crunches the images in input_dir and its subdirectories into output_dir. + + If an image is already optimized, crunching often increases image size. In + this case, the crunched image is overwritten with the original image. + """ + aapt_cmd = [aapt, + 'crunch', + '-C', output_dir, + '-S', input_dir, + '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN] + build_utils.CheckOutput(aapt_cmd, stderr_filter=FilterCrunchStderr, + fail_func=DidCrunchFail) + + # Check for images whose size increased during crunching and replace them + # with their originals (except for 9-patches, which must be crunched). + for dir_, _, files in os.walk(output_dir): + for crunched in files: + if crunched.endswith('.9.png'): + continue + if not crunched.endswith('.png'): + raise Exception('Unexpected file in crunched dir: ' + crunched) + crunched = os.path.join(dir_, crunched) + original = os.path.join(input_dir, os.path.relpath(crunched, output_dir)) + original_size = os.path.getsize(original) + crunched_size = os.path.getsize(crunched) + if original_size < crunched_size: + shutil.copyfile(original, crunched) + + +def FilterCrunchStderr(stderr): + """Filters out lines from aapt crunch's stderr that can safely be ignored.""" + filtered_lines = [] + for line in stderr.splitlines(True): + # Ignore this libpng warning, which is a known non-error condition. + # http://crbug.com/364355 + if ('libpng warning: iCCP: Not recognizing known sRGB profile that has ' + + 'been edited' in line): + continue + filtered_lines.append(line) + return ''.join(filtered_lines) + + +def DidCrunchFail(returncode, stderr): + """Determines whether aapt crunch failed from its return code and output. + + Because aapt's return code cannot be trusted, any output to stderr is + an indication that aapt has failed (http://crbug.com/314885). + """ + return returncode != 0 or stderr + + +def ZipResources(resource_dirs, zip_path): + # Python zipfile does not provide a way to replace a file (it just writes + # another file with the same name). So, first collect all the files to put + # in the zip (with proper overriding), and then zip them. + files_to_zip = dict() + for d in resource_dirs: + for root, _, files in os.walk(d): + for f in files: + archive_path = os.path.join(os.path.relpath(root, d), f) + path = os.path.join(root, f) + files_to_zip[archive_path] = path + with zipfile.ZipFile(zip_path, 'w') as outzip: + for archive_path, path in files_to_zip.iteritems(): + outzip.write(path, archive_path) + + +def CombineZips(zip_files, output_path): + # When packaging resources, if the top-level directories in the zip file are + # of the form 0, 1, ..., then each subdirectory will be passed to aapt as a + # resources directory. While some resources just clobber others (image files, + # etc), other resources (particularly .xml files) need to be more + # intelligently merged. That merging is left up to aapt. + with zipfile.ZipFile(output_path, 'w') as outzip: + for i, z in enumerate(zip_files): + with zipfile.ZipFile(z, 'r') as inzip: + for name in inzip.namelist(): + new_name = '%d/%s' % (i, name) + outzip.writestr(new_name, inzip.read(name)) + + +def main(): + args = build_utils.ExpandFileArgs(sys.argv[1:]) + + options = ParseArgs(args) + android_jar = os.path.join(options.android_sdk, 'android.jar') + aapt = options.aapt_path + + input_files = [] + + with build_utils.TempDir() as temp_dir: + deps_dir = os.path.join(temp_dir, 'deps') + build_utils.MakeDirectory(deps_dir) + v14_dir = os.path.join(temp_dir, 'v14') + build_utils.MakeDirectory(v14_dir) + + gen_dir = os.path.join(temp_dir, 'gen') + build_utils.MakeDirectory(gen_dir) + + input_resource_dirs = build_utils.ParseGypList(options.resource_dirs) + + if not options.v14_skip: + for resource_dir in input_resource_dirs: + generate_v14_compatible_resources.GenerateV14Resources( + resource_dir, + v14_dir) + + dep_zips = build_utils.ParseGypList(options.dependencies_res_zips) + input_files += dep_zips + dep_subdirs = [] + for z in dep_zips: + subdir = os.path.join(deps_dir, os.path.basename(z)) + if os.path.exists(subdir): + raise Exception('Resource zip name conflict: ' + os.path.basename(z)) + build_utils.ExtractAll(z, path=subdir) + dep_subdirs.append(subdir) + + # Generate R.java. This R.java contains non-final constants and is used only + # while compiling the library jar (e.g. chromium_content.jar). When building + # an apk, a new R.java file with the correct resource -> ID mappings will be + # generated by merging the resources from all libraries and the main apk + # project. + package_command = [aapt, + 'package', + '-m', + '-M', options.android_manifest, + '--auto-add-overlay', + '-I', android_jar, + '--output-text-symbols', gen_dir, + '-J', gen_dir, + '--ignore-assets', build_utils.AAPT_IGNORE_PATTERN] + + for d in input_resource_dirs: + package_command += ['-S', d] + + for d in dep_subdirs: + package_command += ['-S', d] + + if options.non_constant_id: + package_command.append('--non-constant-id') + if options.custom_package: + package_command += ['--custom-package', options.custom_package] + if options.proguard_file: + package_command += ['-G', options.proguard_file] + if options.shared_resources: + package_command.append('--shared-lib') + build_utils.CheckOutput(package_command, print_stderr=False) + + if options.extra_res_packages: + CreateExtraRJavaFiles( + gen_dir, + build_utils.ParseGypList(options.extra_res_packages), + build_utils.ParseGypList(options.extra_r_text_files), + options.shared_resources, + options.include_all_resources) + + # This is the list of directories with resources to put in the final .zip + # file. The order of these is important so that crunched/v14 resources + # override the normal ones. + zip_resource_dirs = input_resource_dirs + [v14_dir] + + base_crunch_dir = os.path.join(temp_dir, 'crunch') + + # Crunch image resources. This shrinks png files and is necessary for + # 9-patch images to display correctly. 'aapt crunch' accepts only a single + # directory at a time and deletes everything in the output directory. + for idx, input_dir in enumerate(input_resource_dirs): + crunch_dir = os.path.join(base_crunch_dir, str(idx)) + build_utils.MakeDirectory(crunch_dir) + zip_resource_dirs.append(crunch_dir) + CrunchDirectory(aapt, input_dir, crunch_dir) + + ZipResources(zip_resource_dirs, options.resource_zip_out) + + if options.all_resources_zip_out: + CombineZips([options.resource_zip_out] + dep_zips, + options.all_resources_zip_out) + + if options.R_dir: + build_utils.DeleteDirectory(options.R_dir) + shutil.copytree(gen_dir, options.R_dir) + else: + build_utils.ZipDir(options.srcjar_out, gen_dir) + + if options.r_text_out: + r_text_path = os.path.join(gen_dir, 'R.txt') + if os.path.exists(r_text_path): + shutil.copyfile(r_text_path, options.r_text_out) + else: + open(options.r_text_out, 'w').close() + + if options.depfile: + input_files += build_utils.GetPythonDependencies() + build_utils.WriteDepfile(options.depfile, input_files) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/build/android/gyp/proguard.py b/engine/src/flutter/build/android/gyp/proguard.py new file mode 100755 index 0000000000..5127100a89 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/proguard.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# +# Copyright 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. + +import optparse +import sys + +from util import build_utils +from util import proguard_util + +def DoProguard(options): + proguard = proguard_util.ProguardCmdBuilder(options.proguard_path) + proguard.injars(build_utils.ParseGypList(options.input_paths)) + proguard.configs(build_utils.ParseGypList(options.proguard_configs)) + proguard.outjar(options.output_path) + + if options.mapping: + proguard.mapping(options.mapping) + + if options.is_test: + proguard.is_test(True) + + classpath = [] + for arg in options.classpath: + classpath += build_utils.ParseGypList(arg) + classpath = list(set(classpath)) + proguard.libraryjars(classpath) + + proguard.CheckOutput() + + return proguard.GetInputs() + + +def main(args): + args = build_utils.ExpandFileArgs(args) + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option('--proguard-path', + help='Path to the proguard executable.') + parser.add_option('--input-paths', + help='Paths to the .jar files proguard should run on.') + parser.add_option('--output-path', help='Path to the generated .jar file.') + parser.add_option('--proguard-configs', + help='Paths to proguard configuration files.') + parser.add_option('--mapping', help='Path to proguard mapping to apply.') + parser.add_option('--is-test', action='store_true', + help='If true, extra proguard options for instrumentation tests will be ' + 'added.') + parser.add_option('--classpath', action='append', + help='Classpath for proguard.') + parser.add_option('--stamp', help='Path to touch on success.') + + options, _ = parser.parse_args(args) + + inputs = DoProguard(options) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + inputs + build_utils.GetPythonDependencies()) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/push_libraries.py b/engine/src/flutter/build/android/gyp/push_libraries.py new file mode 100755 index 0000000000..6b31a2e19d --- /dev/null +++ b/engine/src/flutter/build/android/gyp/push_libraries.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# +# Copyright 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. + +"""Pushes native libraries to a device. + +""" + +import optparse +import os +import sys + +BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), os.pardir) +sys.path.append(BUILD_ANDROID_DIR) + +from pylib import constants + +from util import build_device +from util import build_utils +from util import md5_check + +def DoPush(options): + libraries = build_utils.ParseGypList(options.libraries) + + device = build_device.GetBuildDeviceFromPath( + options.build_device_configuration) + if not device: + return + + serial_number = device.GetSerialNumber() + # A list so that it is modifiable in Push below. + needs_directory = [True] + for lib in libraries: + device_path = os.path.join(options.device_dir, lib) + host_path = os.path.join(options.libraries_dir, lib) + + def Push(): + if needs_directory: + device.RunShellCommand('mkdir -p ' + options.device_dir) + needs_directory[:] = [] # = False + device.PushChangedFiles([(host_path, device_path)]) + + record_path = '%s.%s.push.md5.stamp' % (host_path, serial_number) + md5_check.CallAndRecordIfStale( + Push, + record_path=record_path, + input_paths=[host_path], + input_strings=[device_path]) + + +def main(args): + args = build_utils.ExpandFileArgs(args) + parser = optparse.OptionParser() + parser.add_option('--libraries-dir', + help='Directory that contains stripped libraries.') + parser.add_option('--device-dir', + help='Device directory to push the libraries to.') + parser.add_option('--libraries', + help='List of native libraries.') + parser.add_option('--stamp', help='Path to touch on success.') + parser.add_option('--build-device-configuration', + help='Path to build device configuration.') + parser.add_option('--configuration-name', + help='The build CONFIGURATION_NAME') + options, _ = parser.parse_args(args) + + required_options = ['libraries', 'device_dir', 'libraries'] + build_utils.CheckOptions(options, parser, required=required_options) + constants.SetBuildType(options.configuration_name) + + DoPush(options) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/strip_library_for_device.py b/engine/src/flutter/build/android/gyp/strip_library_for_device.py new file mode 100755 index 0000000000..9e2daae33a --- /dev/null +++ b/engine/src/flutter/build/android/gyp/strip_library_for_device.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# +# Copyright 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. + +import optparse +import os +import sys + +from util import build_utils + + +def StripLibrary(android_strip, android_strip_args, library_path, output_path): + if build_utils.IsTimeStale(output_path, [library_path]): + strip_cmd = ([android_strip] + + android_strip_args + + ['-o', output_path, library_path]) + build_utils.CheckOutput(strip_cmd) + + +def main(args): + args = build_utils.ExpandFileArgs(args) + + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option('--android-strip', + help='Path to the toolchain\'s strip binary') + parser.add_option('--android-strip-arg', action='append', + help='Argument to be passed to strip') + parser.add_option('--libraries-dir', + help='Directory for un-stripped libraries') + parser.add_option('--stripped-libraries-dir', + help='Directory for stripped libraries') + parser.add_option('--libraries', + help='List of libraries to strip') + parser.add_option('--stamp', help='Path to touch on success') + + options, _ = parser.parse_args(args) + + libraries = build_utils.ParseGypList(options.libraries) + + build_utils.MakeDirectory(options.stripped_libraries_dir) + + for library in libraries: + for base_path in options.libraries_dir.split(','): + library_path = os.path.join(base_path, library) + if (os.path.exists(library_path)): + break + stripped_library_path = os.path.join( + options.stripped_libraries_dir, library) + StripLibrary(options.android_strip, options.android_strip_arg, library_path, + stripped_library_path) + + if options.stamp: + build_utils.Touch(options.stamp) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/test/BUILD.gn b/engine/src/flutter/build/android/gyp/test/BUILD.gn new file mode 100644 index 0000000000..2deac1d56f --- /dev/null +++ b/engine/src/flutter/build/android/gyp/test/BUILD.gn @@ -0,0 +1,13 @@ +import("//build/config/android/rules.gni") + +java_library("hello_world_java") { + java_files = [ "java/org/chromium/helloworld/HelloWorldPrinter.java" ] +} + +java_binary("hello_world") { + deps = [ + ":hello_world_java", + ] + java_files = [ "java/org/chromium/helloworld/HelloWorldMain.java" ] + main_class = "org.chromium.helloworld.HelloWorldMain" +} diff --git a/engine/src/flutter/build/android/gyp/test/java/org/chromium/helloworld/HelloWorldMain.java b/engine/src/flutter/build/android/gyp/test/java/org/chromium/helloworld/HelloWorldMain.java new file mode 100644 index 0000000000..10860d8332 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/test/java/org/chromium/helloworld/HelloWorldMain.java @@ -0,0 +1,15 @@ +// Copyright 2014 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. + +package org.chromium.helloworld; + +public class HelloWorldMain { + public static void main(String[] args) { + if (args.length > 0) { + System.exit(Integer.parseInt(args[0])); + } + HelloWorldPrinter.print(); + } +} + diff --git a/engine/src/flutter/build/android/gyp/test/java/org/chromium/helloworld/HelloWorldPrinter.java b/engine/src/flutter/build/android/gyp/test/java/org/chromium/helloworld/HelloWorldPrinter.java new file mode 100644 index 0000000000..b09673e21f --- /dev/null +++ b/engine/src/flutter/build/android/gyp/test/java/org/chromium/helloworld/HelloWorldPrinter.java @@ -0,0 +1,12 @@ +// Copyright 2014 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. + +package org.chromium.helloworld; + +public class HelloWorldPrinter { + public static void print() { + System.out.println("Hello, world!"); + } +} + diff --git a/engine/src/flutter/build/android/gyp/touch.py b/engine/src/flutter/build/android/gyp/touch.py new file mode 100755 index 0000000000..7b4375e40a --- /dev/null +++ b/engine/src/flutter/build/android/gyp/touch.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +import sys + +from util import build_utils + +def main(argv): + for f in argv[1:]: + build_utils.Touch(f) + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/gyp/util/__init__.py b/engine/src/flutter/build/android/gyp/util/__init__.py new file mode 100644 index 0000000000..727e987e6b --- /dev/null +++ b/engine/src/flutter/build/android/gyp/util/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2012 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. + diff --git a/engine/src/flutter/build/android/gyp/util/build_device.py b/engine/src/flutter/build/android/gyp/util/build_device.py new file mode 100644 index 0000000000..7e0d57ba38 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/util/build_device.py @@ -0,0 +1,105 @@ +# Copyright 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 simple device interface for build steps. + +""" + +import logging +import os +import re +import sys + +from util import build_utils + +BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..', '..') +sys.path.append(BUILD_ANDROID_DIR) + +from pylib import android_commands +from pylib.device import device_errors +from pylib.device import device_utils + +GetAttachedDevices = android_commands.GetAttachedDevices + + +class BuildDevice(object): + def __init__(self, configuration): + self.id = configuration['id'] + self.description = configuration['description'] + self.install_metadata = configuration['install_metadata'] + self.device = device_utils.DeviceUtils(self.id) + + def RunShellCommand(self, *args, **kwargs): + return self.device.RunShellCommand(*args, **kwargs) + + def PushChangedFiles(self, *args, **kwargs): + return self.device.PushChangedFiles(*args, **kwargs) + + def GetSerialNumber(self): + return self.id + + def Install(self, *args, **kwargs): + return self.device.Install(*args, **kwargs) + + def GetInstallMetadata(self, apk_package): + """Gets the metadata on the device for the apk_package apk.""" + # Matches lines like: + # -rw-r--r-- system system 7376582 2013-04-19 16:34 \ + # org.chromium.chrome.shell.apk + # -rw-r--r-- system system 7376582 2013-04-19 16:34 \ + # org.chromium.chrome.shell-1.apk + apk_matcher = lambda s: re.match('.*%s(-[0-9]*)?.apk$' % apk_package, s) + matches = filter(apk_matcher, self.install_metadata) + return matches[0] if matches else None + + +def GetConfigurationForDevice(device_id): + device = device_utils.DeviceUtils(device_id) + configuration = None + has_root = False + is_online = device.IsOnline() + if is_online: + cmd = 'ls -l /data/app; getprop ro.build.description' + cmd_output = device.RunShellCommand(cmd) + has_root = not 'Permission denied' in cmd_output[0] + if not has_root: + # Disable warning log messages from EnableRoot() + logging.getLogger().disabled = True + try: + device.EnableRoot() + has_root = True + except device_errors.CommandFailedError: + has_root = False + finally: + logging.getLogger().disabled = False + cmd_output = device.RunShellCommand(cmd) + + configuration = { + 'id': device_id, + 'description': cmd_output[-1], + 'install_metadata': cmd_output[:-1], + } + return configuration, is_online, has_root + + +def WriteConfigurations(configurations, path): + # Currently we only support installing to the first device. + build_utils.WriteJson(configurations[:1], path, only_if_changed=True) + + +def ReadConfigurations(path): + return build_utils.ReadJson(path) + + +def GetBuildDevice(configurations): + assert len(configurations) == 1 + return BuildDevice(configurations[0]) + + +def GetBuildDeviceFromPath(path): + configurations = ReadConfigurations(path) + if len(configurations) > 0: + return GetBuildDevice(ReadConfigurations(path)) + return None + diff --git a/engine/src/flutter/build/android/gyp/util/build_utils.py b/engine/src/flutter/build/android/gyp/util/build_utils.py new file mode 100644 index 0000000000..65b1a643c2 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/util/build_utils.py @@ -0,0 +1,376 @@ +# Copyright 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. + +import ast +import contextlib +import fnmatch +import json +import os +import pipes +import re +import shlex +import shutil +import subprocess +import sys +import tempfile +import zipfile + + +CHROMIUM_SRC = os.path.normpath( + os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir, os.pardir, os.pardir)) +COLORAMA_ROOT = os.path.join(CHROMIUM_SRC, + 'third_party', 'colorama', 'src') +# aapt should ignore OWNERS files in addition the default ignore pattern. +AAPT_IGNORE_PATTERN = ('!OWNERS:!.svn:!.git:!.ds_store:!*.scc:.*:_*:' + + '!CVS:!thumbs.db:!picasa.ini:!*~:!*.d.stamp') + + +@contextlib.contextmanager +def TempDir(): + dirname = tempfile.mkdtemp() + try: + yield dirname + finally: + shutil.rmtree(dirname) + + +def MakeDirectory(dir_path): + try: + os.makedirs(dir_path) + except OSError: + pass + + +def DeleteDirectory(dir_path): + if os.path.exists(dir_path): + shutil.rmtree(dir_path) + + +def Touch(path, fail_if_missing=False): + if fail_if_missing and not os.path.exists(path): + raise Exception(path + ' doesn\'t exist.') + + MakeDirectory(os.path.dirname(path)) + with open(path, 'a'): + os.utime(path, None) + + +def FindInDirectory(directory, filename_filter): + files = [] + for root, _dirnames, filenames in os.walk(directory): + matched_files = fnmatch.filter(filenames, filename_filter) + files.extend((os.path.join(root, f) for f in matched_files)) + return files + + +def FindInDirectories(directories, filename_filter): + all_files = [] + for directory in directories: + all_files.extend(FindInDirectory(directory, filename_filter)) + return all_files + + +def ParseGnList(gn_string): + return ast.literal_eval(gn_string) + + +def ParseGypList(gyp_string): + # The ninja generator doesn't support $ in strings, so use ## to + # represent $. + # TODO(cjhopman): Remove when + # https://code.google.com/p/gyp/issues/detail?id=327 + # is addressed. + gyp_string = gyp_string.replace('##', '$') + + if gyp_string.startswith('['): + return ParseGnList(gyp_string) + return shlex.split(gyp_string) + + +def CheckOptions(options, parser, required=None): + if not required: + return + for option_name in required: + if getattr(options, option_name) is None: + parser.error('--%s is required' % option_name.replace('_', '-')) + + +def WriteJson(obj, path, only_if_changed=False): + old_dump = None + if os.path.exists(path): + with open(path, 'r') as oldfile: + old_dump = oldfile.read() + + new_dump = json.dumps(obj, sort_keys=True, indent=2, separators=(',', ': ')) + + if not only_if_changed or old_dump != new_dump: + with open(path, 'w') as outfile: + outfile.write(new_dump) + + +def ReadJson(path): + with open(path, 'r') as jsonfile: + return json.load(jsonfile) + + +class CalledProcessError(Exception): + """This exception is raised when the process run by CheckOutput + exits with a non-zero exit code.""" + + def __init__(self, cwd, args, output): + super(CalledProcessError, self).__init__() + self.cwd = cwd + self.args = args + self.output = output + + def __str__(self): + # A user should be able to simply copy and paste the command that failed + # into their shell. + copyable_command = '( cd {}; {} )'.format(os.path.abspath(self.cwd), + ' '.join(map(pipes.quote, self.args))) + return 'Command failed: {}\n{}'.format(copyable_command, self.output) + + +# This can be used in most cases like subprocess.check_output(). The output, +# particularly when the command fails, better highlights the command's failure. +# If the command fails, raises a build_utils.CalledProcessError. +def CheckOutput(args, cwd=None, + print_stdout=False, print_stderr=True, + stdout_filter=None, + stderr_filter=None, + fail_func=lambda returncode, stderr: returncode != 0): + if not cwd: + cwd = os.getcwd() + + child = subprocess.Popen(args, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) + stdout, stderr = child.communicate() + + if stdout_filter is not None: + stdout = stdout_filter(stdout) + + if stderr_filter is not None: + stderr = stderr_filter(stderr) + + if fail_func(child.returncode, stderr): + raise CalledProcessError(cwd, args, stdout + stderr) + + if print_stdout: + sys.stdout.write(stdout) + if print_stderr: + sys.stderr.write(stderr) + + return stdout + + +def GetModifiedTime(path): + # For a symlink, the modified time should be the greater of the link's + # modified time and the modified time of the target. + return max(os.lstat(path).st_mtime, os.stat(path).st_mtime) + + +def IsTimeStale(output, inputs): + if not os.path.exists(output): + return True + + output_time = GetModifiedTime(output) + for i in inputs: + if GetModifiedTime(i) > output_time: + return True + return False + + +def IsDeviceReady(): + device_state = CheckOutput(['adb', 'get-state']) + return device_state.strip() == 'device' + + +def CheckZipPath(name): + if os.path.normpath(name) != name: + raise Exception('Non-canonical zip path: %s' % name) + if os.path.isabs(name): + raise Exception('Absolute zip path: %s' % name) + + +def ExtractAll(zip_path, path=None, no_clobber=True, pattern=None): + if path is None: + path = os.getcwd() + elif not os.path.exists(path): + MakeDirectory(path) + + with zipfile.ZipFile(zip_path) as z: + for name in z.namelist(): + if name.endswith('/'): + continue + if pattern is not None: + if not fnmatch.fnmatch(name, pattern): + continue + CheckZipPath(name) + if no_clobber: + output_path = os.path.join(path, name) + if os.path.exists(output_path): + raise Exception( + 'Path already exists from zip: %s %s %s' + % (zip_path, name, output_path)) + + z.extractall(path=path) + + +def DoZip(inputs, output, base_dir): + with zipfile.ZipFile(output, 'w') as outfile: + for f in inputs: + CheckZipPath(os.path.relpath(f, base_dir)) + outfile.write(f, os.path.relpath(f, base_dir)) + + +def ZipDir(output, base_dir): + with zipfile.ZipFile(output, 'w') as outfile: + for root, _, files in os.walk(base_dir): + for f in files: + path = os.path.join(root, f) + archive_path = os.path.relpath(path, base_dir) + CheckZipPath(archive_path) + outfile.write(path, archive_path) + + +def MergeZips(output, inputs, exclude_patterns=None): + added_names = set() + def Allow(name): + if exclude_patterns is not None: + for p in exclude_patterns: + if fnmatch.fnmatch(name, p): + return False + return True + + with zipfile.ZipFile(output, 'w') as out_zip: + for in_file in inputs: + with zipfile.ZipFile(in_file, 'r') as in_zip: + for name in in_zip.namelist(): + if name not in added_names and Allow(name): + out_zip.writestr(name, in_zip.read(name)) + added_names.add(name) + + +def PrintWarning(message): + print 'WARNING: ' + message + + +def PrintBigWarning(message): + print '***** ' * 8 + PrintWarning(message) + print '***** ' * 8 + + +def GetSortedTransitiveDependencies(top, deps_func): + """Gets the list of all transitive dependencies in sorted order. + + There should be no cycles in the dependency graph. + + Args: + top: a list of the top level nodes + deps_func: A function that takes a node and returns its direct dependencies. + Returns: + A list of all transitive dependencies of nodes in top, in order (a node will + appear in the list at a higher index than all of its dependencies). + """ + def Node(dep): + return (dep, deps_func(dep)) + + # First: find all deps + unchecked_deps = list(top) + all_deps = set(top) + while unchecked_deps: + dep = unchecked_deps.pop() + new_deps = deps_func(dep).difference(all_deps) + unchecked_deps.extend(new_deps) + all_deps = all_deps.union(new_deps) + + # Then: simple, slow topological sort. + sorted_deps = [] + unsorted_deps = dict(map(Node, all_deps)) + while unsorted_deps: + for library, dependencies in unsorted_deps.items(): + if not dependencies.intersection(unsorted_deps.keys()): + sorted_deps.append(library) + del unsorted_deps[library] + + return sorted_deps + + +def GetPythonDependencies(): + """Gets the paths of imported non-system python modules. + + A path is assumed to be a "system" import if it is outside of chromium's + src/. The paths will be relative to the current directory. + """ + module_paths = (m.__file__ for m in sys.modules.itervalues() + if m is not None and hasattr(m, '__file__')) + + abs_module_paths = map(os.path.abspath, module_paths) + + non_system_module_paths = [ + p for p in abs_module_paths if p.startswith(CHROMIUM_SRC)] + def ConvertPycToPy(s): + if s.endswith('.pyc'): + return s[:-1] + return s + + non_system_module_paths = map(ConvertPycToPy, non_system_module_paths) + non_system_module_paths = map(os.path.relpath, non_system_module_paths) + return sorted(set(non_system_module_paths)) + + +def AddDepfileOption(parser): + parser.add_option('--depfile', + help='Path to depfile. This must be specified as the ' + 'action\'s first output.') + + +def WriteDepfile(path, dependencies): + with open(path, 'w') as depfile: + depfile.write(path) + depfile.write(': ') + depfile.write(' '.join(dependencies)) + depfile.write('\n') + + +def ExpandFileArgs(args): + """Replaces file-arg placeholders in args. + + These placeholders have the form: + @FileArg(filename:key1:key2:...:keyn) + + The value of such a placeholder is calculated by reading 'filename' as json. + And then extracting the value at [key1][key2]...[keyn]. + + Note: This intentionally does not return the list of files that appear in such + placeholders. An action that uses file-args *must* know the paths of those + files prior to the parsing of the arguments (typically by explicitly listing + them in the action's inputs in build files). + """ + new_args = list(args) + file_jsons = dict() + r = re.compile('@FileArg\((.*?)\)') + for i, arg in enumerate(args): + match = r.search(arg) + if not match: + continue + + if match.end() != len(arg): + raise Exception('Unexpected characters after FileArg: ' + arg) + + lookup_path = match.group(1).split(':') + file_path = lookup_path[0] + if not file_path in file_jsons: + file_jsons[file_path] = ReadJson(file_path) + + expansion = file_jsons[file_path] + for k in lookup_path[1:]: + expansion = expansion[k] + + new_args[i] = arg[:match.start()] + str(expansion) + + return new_args + diff --git a/engine/src/flutter/build/android/gyp/util/md5_check.py b/engine/src/flutter/build/android/gyp/util/md5_check.py new file mode 100644 index 0000000000..9f365aa081 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/util/md5_check.py @@ -0,0 +1,86 @@ +# Copyright 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. + +import hashlib +import os + + +def CallAndRecordIfStale( + function, record_path=None, input_paths=None, input_strings=None, + force=False): + """Calls function if the md5sum of the input paths/strings has changed. + + The md5sum of the inputs is compared with the one stored in record_path. If + this has changed (or the record doesn't exist), function will be called and + the new md5sum will be recorded. + + If force is True, the function will be called regardless of whether the + md5sum is out of date. + """ + if not input_paths: + input_paths = [] + if not input_strings: + input_strings = [] + md5_checker = _Md5Checker( + record_path=record_path, + input_paths=input_paths, + input_strings=input_strings) + if force or md5_checker.IsStale(): + function() + md5_checker.Write() + + +def _UpdateMd5ForFile(md5, path, block_size=2**16): + with open(path, 'rb') as infile: + while True: + data = infile.read(block_size) + if not data: + break + md5.update(data) + + +def _UpdateMd5ForDirectory(md5, dir_path): + for root, _, files in os.walk(dir_path): + for f in files: + _UpdateMd5ForFile(md5, os.path.join(root, f)) + + +def _UpdateMd5ForPath(md5, path): + if os.path.isdir(path): + _UpdateMd5ForDirectory(md5, path) + else: + _UpdateMd5ForFile(md5, path) + + +class _Md5Checker(object): + def __init__(self, record_path=None, input_paths=None, input_strings=None): + if not input_paths: + input_paths = [] + if not input_strings: + input_strings = [] + + assert record_path.endswith('.stamp'), ( + 'record paths must end in \'.stamp\' so that they are easy to find ' + 'and delete') + + self.record_path = record_path + + md5 = hashlib.md5() + for i in sorted(input_paths): + _UpdateMd5ForPath(md5, i) + for s in input_strings: + md5.update(s) + self.new_digest = md5.hexdigest() + + self.old_digest = '' + if os.path.exists(self.record_path): + with open(self.record_path, 'r') as old_record: + self.old_digest = old_record.read() + + def IsStale(self): + return self.old_digest != self.new_digest + + def Write(self): + with open(self.record_path, 'w') as new_record: + new_record.write(self.new_digest) diff --git a/engine/src/flutter/build/android/gyp/util/md5_check_test.py b/engine/src/flutter/build/android/gyp/util/md5_check_test.py new file mode 100644 index 0000000000..4f89fc2be8 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/util/md5_check_test.py @@ -0,0 +1,72 @@ +# Copyright 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. + +import tempfile +import unittest + +import md5_check # pylint: disable=W0403 + + +class TestMd5Check(unittest.TestCase): + def setUp(self): + self.called = False + + def testCallAndRecordIfStale(self): + input_strings = ['string1', 'string2'] + input_file1 = tempfile.NamedTemporaryFile() + input_file2 = tempfile.NamedTemporaryFile() + file1_contents = 'input file 1' + file2_contents = 'input file 2' + input_file1.write(file1_contents) + input_file1.flush() + input_file2.write(file2_contents) + input_file2.flush() + input_files = [input_file1.name, input_file2.name] + + record_path = tempfile.NamedTemporaryFile(suffix='.stamp') + + def CheckCallAndRecord(should_call, message, force=False): + self.called = False + def MarkCalled(): + self.called = True + md5_check.CallAndRecordIfStale( + MarkCalled, + record_path=record_path.name, + input_paths=input_files, + input_strings=input_strings, + force=force) + self.failUnlessEqual(should_call, self.called, message) + + CheckCallAndRecord(True, 'should call when record doesn\'t exist') + CheckCallAndRecord(False, 'should not call when nothing changed') + CheckCallAndRecord(True, force=True, message='should call when forced') + + input_file1.write('some more input') + input_file1.flush() + CheckCallAndRecord(True, 'changed input file should trigger call') + + input_files = input_files[::-1] + CheckCallAndRecord(False, 'reordering of inputs shouldn\'t trigger call') + + input_files = input_files[:1] + CheckCallAndRecord(True, 'removing file should trigger call') + + input_files.append(input_file2.name) + CheckCallAndRecord(True, 'added input file should trigger call') + + input_strings[0] = input_strings[0] + ' a bit longer' + CheckCallAndRecord(True, 'changed input string should trigger call') + + input_strings = input_strings[::-1] + CheckCallAndRecord(True, 'reordering of string inputs should trigger call') + + input_strings = input_strings[:1] + CheckCallAndRecord(True, 'removing a string should trigger call') + + input_strings.append('a brand new string') + CheckCallAndRecord(True, 'added input string should trigger call') + + +if __name__ == '__main__': + unittest.main() diff --git a/engine/src/flutter/build/android/gyp/util/proguard_util.py b/engine/src/flutter/build/android/gyp/util/proguard_util.py new file mode 100644 index 0000000000..901cd9f2a8 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/util/proguard_util.py @@ -0,0 +1,128 @@ +# Copyright 2015 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. + +import os +from util import build_utils + +def FilterProguardOutput(output): + '''ProGuard outputs boring stuff to stdout (proguard version, jar path, etc) + as well as interesting stuff (notes, warnings, etc). If stdout is entirely + boring, this method suppresses the output. + ''' + ignore_patterns = [ + 'ProGuard, version ', + 'Reading program jar [', + 'Reading library jar [', + 'Preparing output jar [', + ' Copying resources from program jar [', + ] + for line in output.splitlines(): + for pattern in ignore_patterns: + if line.startswith(pattern): + break + else: + # line doesn't match any of the patterns; it's probably something worth + # printing out. + return output + return '' + + +class ProguardCmdBuilder(object): + def __init__(self, proguard_jar): + assert os.path.exists(proguard_jar) + self._proguard_jar_path = proguard_jar + self._test = None + self._mapping = None + self._libraries = None + self._injars = None + self._configs = None + self._outjar = None + + def outjar(self, path): + assert self._outjar is None + self._outjar = path + + def is_test(self, enable): + assert self._test is None + self._test = enable + + def mapping(self, path): + assert self._mapping is None + assert os.path.exists(path), path + self._mapping = path + + def libraryjars(self, paths): + assert self._libraries is None + for p in paths: + assert os.path.exists(p), p + self._libraries = paths + + def injars(self, paths): + assert self._injars is None + for p in paths: + assert os.path.exists(p), p + self._injars = paths + + def configs(self, paths): + assert self._configs is None + for p in paths: + assert os.path.exists(p), p + self._configs = paths + + def build(self): + assert self._injars is not None + assert self._outjar is not None + assert self._configs is not None + cmd = [ + 'java', '-jar', self._proguard_jar_path, + '-forceprocessing', + ] + if self._test: + cmd += [ + '-dontobfuscate', + '-dontoptimize', + '-dontshrink', + '-dontskipnonpubliclibraryclassmembers', + ] + + if self._mapping: + cmd += [ + '-applymapping', self._mapping, + ] + + if self._libraries: + cmd += [ + '-libraryjars', ':'.join(self._libraries), + ] + + cmd += [ + '-injars', ':'.join(self._injars) + ] + + for config_file in self._configs: + cmd += ['-include', config_file] + + # The output jar must be specified after inputs. + cmd += [ + '-outjars', self._outjar, + '-dump', self._outjar + '.dump', + '-printseeds', self._outjar + '.seeds', + '-printusage', self._outjar + '.usage', + '-printmapping', self._outjar + '.mapping', + ] + return cmd + + def GetInputs(self): + inputs = [self._proguard_jar_path] + self._configs + self._injars + if self._mapping: + inputs.append(self._mapping) + if self._libraries: + inputs += self._libraries + return inputs + + + def CheckOutput(self): + build_utils.CheckOutput(self.build(), print_stdout=True, + stdout_filter=FilterProguardOutput) + diff --git a/engine/src/flutter/build/android/gyp/write_build_config.py b/engine/src/flutter/build/android/gyp/write_build_config.py new file mode 100755 index 0000000000..8507a95d0b --- /dev/null +++ b/engine/src/flutter/build/android/gyp/write_build_config.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Writes a build_config file. + +The build_config file for a target is a json file containing information about +how to build that target based on the target's dependencies. This includes +things like: the javac classpath, the list of android resources dependencies, +etc. It also includes the information needed to create the build_config for +other targets that depend on that one. + +Android build scripts should not refer to the build_config directly, and the +build specification should instead pass information in using the special +file-arg syntax (see build_utils.py:ExpandFileArgs). That syntax allows passing +of values in a json dict in a file and looks like this: + --python-arg=@FileArg(build_config_path:javac:classpath) + +Note: If paths to input files are passed in this way, it is important that: + 1. inputs/deps of the action ensure that the files are available the first + time the action runs. + 2. Either (a) or (b) + a. inputs/deps ensure that the action runs whenever one of the files changes + b. the files are added to the action's depfile +""" + +import optparse +import os +import sys +import xml.dom.minidom + +from util import build_utils + +import write_ordered_libraries + +class AndroidManifest(object): + def __init__(self, path): + self.path = path + dom = xml.dom.minidom.parse(path) + manifests = dom.getElementsByTagName('manifest') + assert len(manifests) == 1 + self.manifest = manifests[0] + + def GetInstrumentation(self): + instrumentation_els = self.manifest.getElementsByTagName('instrumentation') + if len(instrumentation_els) == 0: + return None + if len(instrumentation_els) != 1: + raise Exception( + 'More than one element found in %s' % self.path) + return instrumentation_els[0] + + def CheckInstrumentation(self, expected_package): + instr = self.GetInstrumentation() + if not instr: + raise Exception('No elements found in %s' % self.path) + instrumented_package = instr.getAttributeNS( + 'http://schemas.android.com/apk/res/android', 'targetPackage') + if instrumented_package != expected_package: + raise Exception( + 'Wrong instrumented package. Expected %s, got %s' + % (expected_package, instrumented_package)) + + def GetPackageName(self): + return self.manifest.getAttribute('package') + + +dep_config_cache = {} +def GetDepConfig(path): + if not path in dep_config_cache: + dep_config_cache[path] = build_utils.ReadJson(path)['deps_info'] + return dep_config_cache[path] + + +def DepsOfType(wanted_type, configs): + return [c for c in configs if c['type'] == wanted_type] + + +def GetAllDepsConfigsInOrder(deps_config_paths): + def GetDeps(path): + return set(GetDepConfig(path)['deps_configs']) + return build_utils.GetSortedTransitiveDependencies(deps_config_paths, GetDeps) + + +class Deps(object): + def __init__(self, direct_deps_config_paths): + self.all_deps_config_paths = GetAllDepsConfigsInOrder( + direct_deps_config_paths) + self.direct_deps_configs = [ + GetDepConfig(p) for p in direct_deps_config_paths] + self.all_deps_configs = [ + GetDepConfig(p) for p in self.all_deps_config_paths] + + def All(self, wanted_type=None): + if type is None: + return self.all_deps_configs + return DepsOfType(wanted_type, self.all_deps_configs) + + def Direct(self, wanted_type=None): + if wanted_type is None: + return self.direct_deps_configs + return DepsOfType(wanted_type, self.direct_deps_configs) + + def AllConfigPaths(self): + return self.all_deps_config_paths + + +def main(argv): + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option('--build-config', help='Path to build_config output.') + parser.add_option( + '--type', + help='Type of this target (e.g. android_library).') + parser.add_option( + '--possible-deps-configs', + help='List of paths for dependency\'s build_config files. Some ' + 'dependencies may not write build_config files. Missing build_config ' + 'files are handled differently based on the type of this target.') + + # android_resources options + parser.add_option('--srcjar', help='Path to target\'s resources srcjar.') + parser.add_option('--resources-zip', help='Path to target\'s resources zip.') + parser.add_option('--r-text', help='Path to target\'s R.txt file.') + parser.add_option('--package-name', + help='Java package name for these resources.') + parser.add_option('--android-manifest', help='Path to android manifest.') + + # java library options + parser.add_option('--jar-path', help='Path to target\'s jar output.') + parser.add_option('--supports-android', action='store_true', + help='Whether this library supports running on the Android platform.') + parser.add_option('--requires-android', action='store_true', + help='Whether this library requires running on the Android platform.') + parser.add_option('--bypass-platform-checks', action='store_true', + help='Bypass checks for support/require Android platform.') + + # android library options + parser.add_option('--dex-path', help='Path to target\'s dex output.') + + # native library options + parser.add_option('--native-libs', help='List of top-level native libs.') + parser.add_option('--readelf-path', help='Path to toolchain\'s readelf.') + + parser.add_option('--tested-apk-config', + help='Path to the build config of the tested apk (for an instrumentation ' + 'test apk).') + + options, args = parser.parse_args(argv) + + if args: + parser.error('No positional arguments should be given.') + + + if not options.type in [ + 'java_library', 'android_resources', 'android_apk', 'deps_dex']: + raise Exception('Unknown type: <%s>' % options.type) + + required_options = ['build_config'] + { + 'java_library': ['jar_path'], + 'android_resources': ['resources_zip'], + 'android_apk': ['jar_path', 'dex_path', 'resources_zip'], + 'deps_dex': ['dex_path'] + }[options.type] + + if options.native_libs: + required_options.append('readelf_path') + + build_utils.CheckOptions(options, parser, required_options) + + if options.type == 'java_library': + if options.supports_android and not options.dex_path: + raise Exception('java_library that supports Android requires a dex path.') + + if options.requires_android and not options.supports_android: + raise Exception( + '--supports-android is required when using --requires-android') + + possible_deps_config_paths = build_utils.ParseGypList( + options.possible_deps_configs) + + allow_unknown_deps = options.type == 'android_apk' + unknown_deps = [ + c for c in possible_deps_config_paths if not os.path.exists(c)] + if unknown_deps and not allow_unknown_deps: + raise Exception('Unknown deps: ' + str(unknown_deps)) + + direct_deps_config_paths = [ + c for c in possible_deps_config_paths if not c in unknown_deps] + + deps = Deps(direct_deps_config_paths) + direct_library_deps = deps.Direct('java_library') + all_library_deps = deps.All('java_library') + + direct_resources_deps = deps.Direct('android_resources') + all_resources_deps = deps.All('android_resources') + # Resources should be ordered with the highest-level dependency first so that + # overrides are done correctly. + all_resources_deps.reverse() + + if options.type == 'android_apk' and options.tested_apk_config: + tested_apk_deps = Deps([options.tested_apk_config]) + tested_apk_resources_deps = tested_apk_deps.All('android_resources') + all_resources_deps = [ + d for d in all_resources_deps if not d in tested_apk_resources_deps] + + # Initialize some common config. + config = { + 'deps_info': { + 'name': os.path.basename(options.build_config), + 'path': options.build_config, + 'type': options.type, + 'deps_configs': direct_deps_config_paths, + } + } + deps_info = config['deps_info'] + + if options.type == 'java_library' and not options.bypass_platform_checks: + deps_info['requires_android'] = options.requires_android + deps_info['supports_android'] = options.supports_android + + deps_require_android = (all_resources_deps + + [d['name'] for d in all_library_deps if d['requires_android']]) + deps_not_support_android = ( + [d['name'] for d in all_library_deps if not d['supports_android']]) + + if deps_require_android and not options.requires_android: + raise Exception('Some deps require building for the Android platform: ' + + str(deps_require_android)) + + if deps_not_support_android and options.supports_android: + raise Exception('Not all deps support the Android platform: ' + + str(deps_not_support_android)) + + if options.type in ['java_library', 'android_apk']: + javac_classpath = [c['jar_path'] for c in direct_library_deps] + java_full_classpath = [c['jar_path'] for c in all_library_deps] + deps_info['resources_deps'] = [c['path'] for c in all_resources_deps] + deps_info['jar_path'] = options.jar_path + if options.type == 'android_apk' or options.supports_android: + deps_info['dex_path'] = options.dex_path + config['javac'] = { + 'classpath': javac_classpath, + } + config['java'] = { + 'full_classpath': java_full_classpath + } + + if options.type == 'java_library': + # Only resources might have srcjars (normal srcjar targets are listed in + # srcjar_deps). A resource's srcjar contains the R.java file for those + # resources, and (like Android's default build system) we allow a library to + # refer to the resources in any of its dependents. + config['javac']['srcjars'] = [ + c['srcjar'] for c in direct_resources_deps if 'srcjar' in c] + + if options.type == 'android_apk': + # Apks will get their resources srcjar explicitly passed to the java step. + config['javac']['srcjars'] = [] + + if options.type == 'android_resources': + deps_info['resources_zip'] = options.resources_zip + if options.srcjar: + deps_info['srcjar'] = options.srcjar + if options.android_manifest: + manifest = AndroidManifest(options.android_manifest) + deps_info['package_name'] = manifest.GetPackageName() + if options.package_name: + deps_info['package_name'] = options.package_name + if options.r_text: + deps_info['r_text'] = options.r_text + + if options.type == 'android_resources' or options.type == 'android_apk': + config['resources'] = {} + config['resources']['dependency_zips'] = [ + c['resources_zip'] for c in all_resources_deps] + config['resources']['extra_package_names'] = [] + config['resources']['extra_r_text_files'] = [] + + if options.type == 'android_apk': + config['resources']['extra_package_names'] = [ + c['package_name'] for c in all_resources_deps if 'package_name' in c] + config['resources']['extra_r_text_files'] = [ + c['r_text'] for c in all_resources_deps if 'r_text' in c] + + if options.type in ['android_apk', 'deps_dex']: + deps_dex_files = [c['dex_path'] for c in all_library_deps] + + # An instrumentation test apk should exclude the dex files that are in the apk + # under test. + if options.type == 'android_apk' and options.tested_apk_config: + tested_apk_deps = Deps([options.tested_apk_config]) + tested_apk_library_deps = tested_apk_deps.All('java_library') + tested_apk_deps_dex_files = [c['dex_path'] for c in tested_apk_library_deps] + deps_dex_files = [ + p for p in deps_dex_files if not p in tested_apk_deps_dex_files] + + tested_apk_config = GetDepConfig(options.tested_apk_config) + expected_tested_package = tested_apk_config['package_name'] + AndroidManifest(options.android_manifest).CheckInstrumentation( + expected_tested_package) + + # Dependencies for the final dex file of an apk or a 'deps_dex'. + if options.type in ['android_apk', 'deps_dex']: + config['final_dex'] = {} + dex_config = config['final_dex'] + # TODO(cjhopman): proguard version + dex_config['dependency_dex_files'] = deps_dex_files + + if options.type == 'android_apk': + config['dist_jar'] = { + 'dependency_jars': [ + c['jar_path'] for c in all_library_deps + ] + } + manifest = AndroidManifest(options.android_manifest) + deps_info['package_name'] = manifest.GetPackageName() + if not options.tested_apk_config and manifest.GetInstrumentation(): + # This must then have instrumentation only for itself. + manifest.CheckInstrumentation(manifest.GetPackageName()) + + library_paths = [] + java_libraries_list = [] + if options.native_libs: + libraries = build_utils.ParseGypList(options.native_libs) + if libraries: + libraries_dir = os.path.dirname(libraries[0]) + write_ordered_libraries.SetReadelfPath(options.readelf_path) + write_ordered_libraries.SetLibraryDirs([libraries_dir]) + all_native_library_deps = ( + write_ordered_libraries.GetSortedTransitiveDependenciesForBinaries( + libraries)) + # Create a java literal array with the "base" library names: + # e.g. libfoo.so -> foo + java_libraries_list = '{%s}' % ','.join( + ['"%s"' % s[3:-3] for s in all_native_library_deps]) + library_paths = map( + write_ordered_libraries.FullLibraryPath, all_native_library_deps) + + config['native'] = { + 'libraries': library_paths, + 'java_libraries_list': java_libraries_list + } + + build_utils.WriteJson(config, options.build_config, only_if_changed=True) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + deps.AllConfigPaths() + build_utils.GetPythonDependencies()) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/android/gyp/write_ordered_libraries.py b/engine/src/flutter/build/android/gyp/write_ordered_libraries.py new file mode 100755 index 0000000000..67e7805cd2 --- /dev/null +++ b/engine/src/flutter/build/android/gyp/write_ordered_libraries.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# +# Copyright 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. + +"""Writes dependency ordered list of native libraries. + +The list excludes any Android system libraries, as those are not bundled with +the APK. + +This list of libraries is used for several steps of building an APK. +In the component build, the --input-libraries only needs to be the top-level +library (i.e. libcontent_shell_content_view). This will then use readelf to +inspect the shared libraries and determine the full list of (non-system) +libraries that should be included in the APK. +""" + +# TODO(cjhopman): See if we can expose the list of library dependencies from +# gyp, rather than calculating it ourselves. +# http://crbug.com/225558 + +import optparse +import os +import re +import sys + +from util import build_utils + +_readelf = None +_library_dirs = None + +_library_re = re.compile( + '.*NEEDED.*Shared library: \[(?P.+)\]') + + +def SetReadelfPath(path): + global _readelf + _readelf = path + + +def SetLibraryDirs(dirs): + global _library_dirs + _library_dirs = dirs + + +def FullLibraryPath(library_name): + assert _library_dirs is not None + for directory in _library_dirs: + path = '%s/%s' % (directory, library_name) + if os.path.exists(path): + return path + return library_name + + +def IsSystemLibrary(library_name): + # If the library doesn't exist in the libraries directory, assume that it is + # an Android system library. + return not os.path.exists(FullLibraryPath(library_name)) + + +def CallReadElf(library_or_executable): + assert _readelf is not None + readelf_cmd = [_readelf, + '-d', + FullLibraryPath(library_or_executable)] + return build_utils.CheckOutput(readelf_cmd) + + +def GetDependencies(library_or_executable): + elf = CallReadElf(library_or_executable) + return set(_library_re.findall(elf)) + + +def GetNonSystemDependencies(library_name): + all_deps = GetDependencies(FullLibraryPath(library_name)) + return set((lib for lib in all_deps if not IsSystemLibrary(lib))) + + +def GetSortedTransitiveDependencies(libraries): + """Returns all transitive library dependencies in dependency order.""" + return build_utils.GetSortedTransitiveDependencies( + libraries, GetNonSystemDependencies) + + +def GetSortedTransitiveDependenciesForBinaries(binaries): + if binaries[0].endswith('.so'): + libraries = [os.path.basename(lib) for lib in binaries] + else: + assert len(binaries) == 1 + all_deps = GetDependencies(binaries[0]) + libraries = [lib for lib in all_deps if not IsSystemLibrary(lib)] + + return GetSortedTransitiveDependencies(libraries) + + +def main(): + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + + parser.add_option('--input-libraries', + help='A list of top-level input libraries.') + parser.add_option('--libraries-dir', + help='The directory which contains shared libraries.') + parser.add_option('--readelf', help='Path to the readelf binary.') + parser.add_option('--output', help='Path to the generated .json file.') + parser.add_option('--stamp', help='Path to touch on success.') + + options, _ = parser.parse_args() + + SetReadelfPath(options.readelf) + SetLibraryDirs(options.libraries_dir.split(',')) + + libraries = build_utils.ParseGypList(options.input_libraries) + if len(libraries): + libraries = GetSortedTransitiveDependenciesForBinaries(libraries) + + # Convert to "base" library names: e.g. libfoo.so -> foo + java_libraries_list = ( + '{%s}' % ','.join(['"%s"' % s[3:-3] for s in libraries])) + + build_utils.WriteJson( + {'libraries': libraries, 'java_libraries_list': java_libraries_list}, + options.output, + only_if_changed=True) + + if options.stamp: + build_utils.Touch(options.stamp) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + libraries + build_utils.GetPythonDependencies()) + + +if __name__ == '__main__': + sys.exit(main()) + + diff --git a/engine/src/flutter/build/android/gyp/zip.py b/engine/src/flutter/build/android/gyp/zip.py new file mode 100755 index 0000000000..51322dfd5b --- /dev/null +++ b/engine/src/flutter/build/android/gyp/zip.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Archives a set of files. +""" + +import optparse +import sys + +from util import build_utils + +def main(): + parser = optparse.OptionParser() + parser.add_option('--input-dir', help='Directory of files to archive.') + parser.add_option('--output', help='Path to output archive.') + options, _ = parser.parse_args() + + inputs = build_utils.FindInDirectory(options.input_dir, '*') + build_utils.DoZip(inputs, options.output, options.input_dir) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/host_heartbeat.py b/engine/src/flutter/build/android/host_heartbeat.py new file mode 100755 index 0000000000..6a7cdd1d8e --- /dev/null +++ b/engine/src/flutter/build/android/host_heartbeat.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# +# 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. + +"""Sends a heart beat pulse to the currently online Android devices. +This heart beat lets the devices know that they are connected to a host. +""" +# pylint: disable=W0702 + +import sys +import time + +from pylib.device import device_utils + +PULSE_PERIOD = 20 + +def main(): + while True: + try: + devices = device_utils.DeviceUtils.HealthyDevices() + for d in devices: + d.RunShellCommand(['touch', '/sdcard/host_heartbeat'], + check_return=True) + except: + # Keep the heatbeat running bypassing all errors. + pass + time.sleep(PULSE_PERIOD) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/install_emulator_deps.py b/engine/src/flutter/build/android/install_emulator_deps.py new file mode 100755 index 0000000000..82d1c75137 --- /dev/null +++ b/engine/src/flutter/build/android/install_emulator_deps.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python +# 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. + +"""Installs deps for using SDK emulator for testing. + +The script will download the SDK and system images, if they are not present, and +install and enable KVM, if virtualization has been enabled in the BIOS. +""" + + +import logging +import optparse +import os +import re +import shutil +import sys + +from pylib import cmd_helper +from pylib import constants +from pylib import pexpect +from pylib.utils import run_tests_helper + +# Android API level +DEFAULT_ANDROID_API_LEVEL = constants.ANDROID_SDK_VERSION + +# From the Android Developer's website. +# Keep this up to date; the user can install older API levels as necessary. +SDK_BASE_URL = 'http://dl.google.com/android/adt' +SDK_ZIP = 'adt-bundle-linux-x86_64-20131030.zip' + +# pylint: disable=line-too-long +# Android x86 system image from the Intel website: +# http://software.intel.com/en-us/articles/intel-eula-x86-android-4-2-jelly-bean-bin +# These don't exist prior to Android-15. +# As of 08 Nov 2013, Android-19 is not yet available either. +X86_IMG_URLS = { + 15: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-15_r01.zip', + 16: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-16_r01.zip', + 17: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-17_r01.zip', + 18: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-18_r01.zip', + 19: 'https://software.intel.com/sites/landingpage/android/sysimg_x86-19_r01.zip'} +#pylint: enable=line-too-long + +def CheckSDK(): + """Check if SDK is already installed. + + Returns: + True if the emulator SDK directory (src/android_emulator_sdk/) exists. + """ + return os.path.exists(constants.EMULATOR_SDK_ROOT) + + +def CheckSDKPlatform(api_level=DEFAULT_ANDROID_API_LEVEL): + """Check if the "SDK Platform" for the specified API level is installed. + This is necessary in order for the emulator to run when the target + is specified. + + Args: + api_level: the Android API level to check; defaults to the latest API. + + Returns: + True if the platform is already installed. + """ + android_binary = os.path.join(constants.EMULATOR_SDK_ROOT, + 'sdk', 'tools', 'android') + pattern = re.compile('id: [0-9]+ or "android-%d"' % api_level) + try: + exit_code, stdout = cmd_helper.GetCmdStatusAndOutput( + [android_binary, 'list']) + if exit_code != 0: + raise Exception('\'android list\' command failed') + for line in stdout.split('\n'): + if pattern.match(line): + return True + return False + except OSError: + logging.exception('Unable to execute \'android list\'') + return False + + +def CheckX86Image(api_level=DEFAULT_ANDROID_API_LEVEL): + """Check if Android system images have been installed. + + Args: + api_level: the Android API level to check for; defaults to the latest API. + + Returns: + True if sdk/system-images/android-/x86 exists inside + EMULATOR_SDK_ROOT. + """ + api_target = 'android-%d' % api_level + return os.path.exists(os.path.join(constants.EMULATOR_SDK_ROOT, + 'sdk', 'system-images', + api_target, 'x86')) + + +def CheckKVM(): + """Quickly check whether KVM is enabled. + + Returns: + True iff /dev/kvm exists (Linux only). + """ + return os.path.exists('/dev/kvm') + + +def RunKvmOk(): + """Run kvm-ok as root to check that KVM is properly enabled after installation + of the required packages. + + Returns: + True iff KVM is enabled (/dev/kvm exists). On failure, returns False + but also print detailed information explaining why KVM isn't enabled + (e.g. CPU doesn't support it, or BIOS disabled it). + """ + try: + # Note: kvm-ok is in /usr/sbin, so always use 'sudo' to run it. + return not cmd_helper.RunCmd(['sudo', 'kvm-ok']) + except OSError: + logging.info('kvm-ok not installed') + return False + + +def GetSDK(): + """Download the SDK and unzip it into EMULATOR_SDK_ROOT.""" + logging.info('Download Android SDK.') + sdk_url = '%s/%s' % (SDK_BASE_URL, SDK_ZIP) + try: + cmd_helper.RunCmd(['curl', '-o', '/tmp/sdk.zip', sdk_url]) + print 'curled unzipping...' + rc = cmd_helper.RunCmd(['unzip', '-o', '/tmp/sdk.zip', '-d', '/tmp/']) + if rc: + raise Exception('ERROR: could not download/unzip Android SDK.') + # Get the name of the sub-directory that everything will be extracted to. + dirname, _ = os.path.splitext(SDK_ZIP) + zip_dir = '/tmp/%s' % dirname + # Move the extracted directory to EMULATOR_SDK_ROOT + shutil.move(zip_dir, constants.EMULATOR_SDK_ROOT) + finally: + os.unlink('/tmp/sdk.zip') + + +def InstallKVM(): + """Installs KVM packages.""" + rc = cmd_helper.RunCmd(['sudo', 'apt-get', 'install', 'kvm']) + if rc: + logging.critical('ERROR: Did not install KVM. Make sure hardware ' + 'virtualization is enabled in BIOS (i.e. Intel VT-x or ' + 'AMD SVM).') + # TODO(navabi): Use modprobe kvm-amd on AMD processors. + rc = cmd_helper.RunCmd(['sudo', 'modprobe', 'kvm-intel']) + if rc: + logging.critical('ERROR: Did not add KVM module to Linux Kernel. Make sure ' + 'hardware virtualization is enabled in BIOS.') + # Now check to ensure KVM acceleration can be used. + if not RunKvmOk(): + logging.critical('ERROR: Can not use KVM acceleration. Make sure hardware ' + 'virtualization is enabled in BIOS (i.e. Intel VT-x or ' + 'AMD SVM).') + + +def GetX86Image(api_level=DEFAULT_ANDROID_API_LEVEL): + """Download x86 system image from Intel's website. + + Args: + api_level: the Android API level to download for. + """ + logging.info('Download x86 system image directory into sdk directory.') + # TODO(andrewhayden): Use python tempfile lib instead + temp_file = '/tmp/x86_img_android-%d.zip' % api_level + if api_level not in X86_IMG_URLS: + raise Exception('ERROR: no URL known for x86 image for android-%s' % + api_level) + try: + cmd_helper.RunCmd(['curl', '-o', temp_file, X86_IMG_URLS[api_level]]) + rc = cmd_helper.RunCmd(['unzip', '-o', temp_file, '-d', '/tmp/']) + if rc: + raise Exception('ERROR: Could not download/unzip image zip.') + api_target = 'android-%d' % api_level + sys_imgs = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk', + 'system-images', api_target, 'x86') + logging.info('Deploying system image to %s' % sys_imgs) + shutil.move('/tmp/x86', sys_imgs) + finally: + os.unlink(temp_file) + + +def GetSDKPlatform(api_level=DEFAULT_ANDROID_API_LEVEL): + """Update the SDK to include the platform specified. + + Args: + api_level: the Android API level to download + """ + android_binary = os.path.join(constants.EMULATOR_SDK_ROOT, + 'sdk', 'tools', 'android') + pattern = re.compile( + r'\s*([0-9]+)- SDK Platform Android [\.,0-9]+, API %d.*' % api_level) + # Example: + # 2- SDK Platform Android 4.3, API 18, revision 2 + exit_code, stdout = cmd_helper.GetCmdStatusAndOutput( + [android_binary, 'list', 'sdk']) + if exit_code != 0: + raise Exception('\'android list sdk\' command return %d' % exit_code) + for line in stdout.split('\n'): + match = pattern.match(line) + if match: + index = match.group(1) + print 'package %s corresponds to platform level %d' % (index, api_level) + # update sdk --no-ui --filter $INDEX + update_command = [android_binary, + 'update', 'sdk', '--no-ui', '--filter', index] + update_command_str = ' '.join(update_command) + logging.info('running update command: %s' % update_command_str) + update_process = pexpect.spawn(update_command_str) + # TODO(andrewhayden): Do we need to bug the user about this? + if update_process.expect('Do you accept the license') != 0: + raise Exception('License agreement check failed') + update_process.sendline('y') + if update_process.expect('Done. 1 package installed.') == 0: + print 'Successfully installed platform for API level %d' % api_level + return + else: + raise Exception('Failed to install platform update') + raise Exception('Could not find android-%d update for the SDK!' % api_level) + + +def main(argv): + opt_parser = optparse.OptionParser( + description='Install dependencies for running the Android emulator') + opt_parser.add_option('--api-level', dest='api_level', + help='The API level (e.g., 19 for Android 4.4) to ensure is available', + type='int', default=DEFAULT_ANDROID_API_LEVEL) + opt_parser.add_option('-v', dest='verbose', action='store_true', + help='enable verbose logging') + options, _ = opt_parser.parse_args(argv[1:]) + + # run_tests_helper will set logging to INFO or DEBUG + # We achieve verbose output by configuring it with 2 (==DEBUG) + verbosity = 1 + if options.verbose: + verbosity = 2 + logging.basicConfig(level=logging.INFO, + format='# %(asctime)-15s: %(message)s') + run_tests_helper.SetLogLevel(verbose_count=verbosity) + + # Calls below will download emulator SDK and/or system images only if needed. + if CheckSDK(): + logging.info('android_emulator_sdk/ already exists, skipping download.') + else: + GetSDK() + + # Check target. The target has to be installed in order to run the emulator. + if CheckSDKPlatform(options.api_level): + logging.info('SDK platform android-%d already present, skipping.' % + options.api_level) + else: + logging.info('SDK platform android-%d not present, installing.' % + options.api_level) + GetSDKPlatform(options.api_level) + + # Download the x86 system image only if needed. + if CheckX86Image(options.api_level): + logging.info('x86 image for android-%d already present, skipping.' % + options.api_level) + else: + GetX86Image(options.api_level) + + # Make sure KVM packages are installed and enabled. + if CheckKVM(): + logging.info('KVM already installed and enabled.') + else: + InstallKVM() + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/lighttpd_server.py b/engine/src/flutter/build/android/lighttpd_server.py new file mode 100755 index 0000000000..a5195ac815 --- /dev/null +++ b/engine/src/flutter/build/android/lighttpd_server.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python +# +# Copyright (c) 2012 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. + +"""Provides a convenient wrapper for spawning a test lighttpd instance. + +Usage: + lighttpd_server PATH_TO_DOC_ROOT +""" + +import codecs +import contextlib +import httplib +import os +import random +import shutil +import socket +import subprocess +import sys +import tempfile +import time + +from pylib import constants +from pylib import pexpect + +class LighttpdServer(object): + """Wraps lighttpd server, providing robust startup. + + Args: + document_root: Path to root of this server's hosted files. + port: TCP port on the _host_ machine that the server will listen on. If + ommitted it will attempt to use 9000, or if unavailable it will find + a free port from 8001 - 8999. + lighttpd_path, lighttpd_module_path: Optional paths to lighttpd binaries. + base_config_path: If supplied this file will replace the built-in default + lighttpd config file. + extra_config_contents: If specified, this string will be appended to the + base config (default built-in, or from base_config_path). + config_path, error_log, access_log: Optional paths where the class should + place temprary files for this session. + """ + + def __init__(self, document_root, port=None, + lighttpd_path=None, lighttpd_module_path=None, + base_config_path=None, extra_config_contents=None, + config_path=None, error_log=None, access_log=None): + self.temp_dir = tempfile.mkdtemp(prefix='lighttpd_for_chrome_android') + self.document_root = os.path.abspath(document_root) + self.fixed_port = port + self.port = port or constants.LIGHTTPD_DEFAULT_PORT + self.server_tag = 'LightTPD ' + str(random.randint(111111, 999999)) + self.lighttpd_path = lighttpd_path or '/usr/sbin/lighttpd' + self.lighttpd_module_path = lighttpd_module_path or '/usr/lib/lighttpd' + self.base_config_path = base_config_path + self.extra_config_contents = extra_config_contents + self.config_path = config_path or self._Mktmp('config') + self.error_log = error_log or self._Mktmp('error_log') + self.access_log = access_log or self._Mktmp('access_log') + self.pid_file = self._Mktmp('pid_file') + self.process = None + + def _Mktmp(self, name): + return os.path.join(self.temp_dir, name) + + @staticmethod + def _GetRandomPort(): + # The ports of test server is arranged in constants.py. + return random.randint(constants.LIGHTTPD_RANDOM_PORT_FIRST, + constants.LIGHTTPD_RANDOM_PORT_LAST) + + def StartupHttpServer(self): + """Starts up a http server with specified document root and port.""" + # If we want a specific port, make sure no one else is listening on it. + if self.fixed_port: + self._KillProcessListeningOnPort(self.fixed_port) + while True: + if self.base_config_path: + # Read the config + with codecs.open(self.base_config_path, 'r', 'utf-8') as f: + config_contents = f.read() + else: + config_contents = self._GetDefaultBaseConfig() + if self.extra_config_contents: + config_contents += self.extra_config_contents + # Write out the config, filling in placeholders from the members of |self| + with codecs.open(self.config_path, 'w', 'utf-8') as f: + f.write(config_contents % self.__dict__) + if (not os.path.exists(self.lighttpd_path) or + not os.access(self.lighttpd_path, os.X_OK)): + raise EnvironmentError( + 'Could not find lighttpd at %s.\n' + 'It may need to be installed (e.g. sudo apt-get install lighttpd)' + % self.lighttpd_path) + self.process = pexpect.spawn(self.lighttpd_path, + ['-D', '-f', self.config_path, + '-m', self.lighttpd_module_path], + cwd=self.temp_dir) + client_error, server_error = self._TestServerConnection() + if not client_error: + assert int(open(self.pid_file, 'r').read()) == self.process.pid + break + self.process.close() + + if self.fixed_port or not 'in use' in server_error: + print 'Client error:', client_error + print 'Server error:', server_error + return False + self.port = self._GetRandomPort() + return True + + def ShutdownHttpServer(self): + """Shuts down our lighttpd processes.""" + if self.process: + self.process.terminate() + shutil.rmtree(self.temp_dir, ignore_errors=True) + + def _TestServerConnection(self): + # Wait for server to start + server_msg = '' + for timeout in xrange(1, 5): + client_error = None + try: + with contextlib.closing(httplib.HTTPConnection( + '127.0.0.1', self.port, timeout=timeout)) as http: + http.set_debuglevel(timeout > 3) + http.request('HEAD', '/') + r = http.getresponse() + r.read() + if (r.status == 200 and r.reason == 'OK' and + r.getheader('Server') == self.server_tag): + return (None, server_msg) + client_error = ('Bad response: %s %s version %s\n ' % + (r.status, r.reason, r.version) + + '\n '.join([': '.join(h) for h in r.getheaders()])) + except (httplib.HTTPException, socket.error) as client_error: + pass # Probably too quick connecting: try again + # Check for server startup error messages + ix = self.process.expect([pexpect.TIMEOUT, pexpect.EOF, '.+'], + timeout=timeout) + if ix == 2: # stdout spew from the server + server_msg += self.process.match.group(0) + elif ix == 1: # EOF -- server has quit so giveup. + client_error = client_error or 'Server exited' + break + return (client_error or 'Timeout', server_msg) + + @staticmethod + def _KillProcessListeningOnPort(port): + """Checks if there is a process listening on port number |port| and + terminates it if found. + + Args: + port: Port number to check. + """ + if subprocess.call(['fuser', '-kv', '%d/tcp' % port]) == 0: + # Give the process some time to terminate and check that it is gone. + time.sleep(2) + assert subprocess.call(['fuser', '-v', '%d/tcp' % port]) != 0, \ + 'Unable to kill process listening on port %d.' % port + + @staticmethod + def _GetDefaultBaseConfig(): + return """server.tag = "%(server_tag)s" +server.modules = ( "mod_access", + "mod_accesslog", + "mod_alias", + "mod_cgi", + "mod_rewrite" ) + +# default document root required +#server.document-root = "." + +# files to check for if .../ is requested +index-file.names = ( "index.php", "index.pl", "index.cgi", + "index.html", "index.htm", "default.htm" ) +# mimetype mapping +mimetype.assign = ( + ".gif" => "image/gif", + ".jpg" => "image/jpeg", + ".jpeg" => "image/jpeg", + ".png" => "image/png", + ".svg" => "image/svg+xml", + ".css" => "text/css", + ".html" => "text/html", + ".htm" => "text/html", + ".xhtml" => "application/xhtml+xml", + ".xhtmlmp" => "application/vnd.wap.xhtml+xml", + ".js" => "application/x-javascript", + ".log" => "text/plain", + ".conf" => "text/plain", + ".text" => "text/plain", + ".txt" => "text/plain", + ".dtd" => "text/xml", + ".xml" => "text/xml", + ".manifest" => "text/cache-manifest", + ) + +# Use the "Content-Type" extended attribute to obtain mime type if possible +mimetype.use-xattr = "enable" + +## +# which extensions should not be handle via static-file transfer +# +# .php, .pl, .fcgi are most often handled by mod_fastcgi or mod_cgi +static-file.exclude-extensions = ( ".php", ".pl", ".cgi" ) + +server.bind = "127.0.0.1" +server.port = %(port)s + +## virtual directory listings +dir-listing.activate = "enable" +#dir-listing.encoding = "iso-8859-2" +#dir-listing.external-css = "style/oldstyle.css" + +## enable debugging +#debug.log-request-header = "enable" +#debug.log-response-header = "enable" +#debug.log-request-handling = "enable" +#debug.log-file-not-found = "enable" + +#### SSL engine +#ssl.engine = "enable" +#ssl.pemfile = "server.pem" + +# Autogenerated test-specific config follows. + +cgi.assign = ( ".cgi" => "/usr/bin/env", + ".pl" => "/usr/bin/env", + ".asis" => "/bin/cat", + ".php" => "/usr/bin/php-cgi" ) + +server.errorlog = "%(error_log)s" +accesslog.filename = "%(access_log)s" +server.upload-dirs = ( "/tmp" ) +server.pid-file = "%(pid_file)s" +server.document-root = "%(document_root)s" + +""" + + +def main(argv): + server = LighttpdServer(*argv[1:]) + try: + if server.StartupHttpServer(): + raw_input('Server running at http://127.0.0.1:%s -' + ' press Enter to exit it.' % server.port) + else: + print 'Server exit code:', server.process.exitstatus + finally: + server.ShutdownHttpServer() + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/android/lint/OWNERS b/engine/src/flutter/build/android/lint/OWNERS new file mode 100644 index 0000000000..cd396e7e57 --- /dev/null +++ b/engine/src/flutter/build/android/lint/OWNERS @@ -0,0 +1,2 @@ +newt@chromium.org +aurimas@chromium.org diff --git a/engine/src/flutter/build/android/lint/suppress.py b/engine/src/flutter/build/android/lint/suppress.py new file mode 100755 index 0000000000..52d7579b96 --- /dev/null +++ b/engine/src/flutter/build/android/lint/suppress.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# +# 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. + +"""Add all generated lint_result.xml files to suppressions.xml""" + + +import collections +import optparse +import os +import sys +from xml.dom import minidom + +_BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..') +sys.path.append(_BUILD_ANDROID_DIR) + +from pylib import constants + + +_THIS_FILE = os.path.abspath(__file__) +_CONFIG_PATH = os.path.join(os.path.dirname(_THIS_FILE), 'suppressions.xml') +_DOC = ( + '\nSTOP! It looks like you want to suppress some lint errors:\n' + '- Have you tried identifing the offending patch?\n' + ' Ask the author for a fix and/or revert the patch.\n' + '- It is preferred to add suppressions in the code instead of\n' + ' sweeping it under the rug here. See:\n\n' + ' http://developer.android.com/tools/debugging/improving-w-lint.html\n' + '\n' + 'Still reading?\n' + '- You can edit this file manually to suppress an issue\n' + ' globally if it is not applicable to the project.\n' + '- You can also automatically add issues found so for in the\n' + ' build process by running:\n\n' + ' ' + os.path.relpath(_THIS_FILE, constants.DIR_SOURCE_ROOT) + '\n\n' + ' which will generate this file (Comments are not preserved).\n' + ' Note: PRODUCT_DIR will be substituted at run-time with actual\n' + ' directory path (e.g. out/Debug)\n' +) + + +_Issue = collections.namedtuple('Issue', ['severity', 'paths']) + + +def _ParseConfigFile(config_path): + print 'Parsing %s' % config_path + issues_dict = {} + dom = minidom.parse(config_path) + for issue in dom.getElementsByTagName('issue'): + issue_id = issue.attributes['id'].value + severity = issue.getAttribute('severity') + paths = set( + [p.attributes['path'].value for p in + issue.getElementsByTagName('ignore')]) + issues_dict[issue_id] = _Issue(severity, paths) + return issues_dict + + +def _ParseAndMergeResultFile(result_path, issues_dict): + print 'Parsing and merging %s' % result_path + dom = minidom.parse(result_path) + for issue in dom.getElementsByTagName('issue'): + issue_id = issue.attributes['id'].value + severity = issue.attributes['severity'].value + path = issue.getElementsByTagName('location')[0].attributes['file'].value + if issue_id not in issues_dict: + issues_dict[issue_id] = _Issue(severity, set()) + issues_dict[issue_id].paths.add(path) + + +def _WriteConfigFile(config_path, issues_dict): + new_dom = minidom.getDOMImplementation().createDocument(None, 'lint', None) + top_element = new_dom.documentElement + top_element.appendChild(new_dom.createComment(_DOC)) + for issue_id in sorted(issues_dict.keys()): + severity = issues_dict[issue_id].severity + paths = issues_dict[issue_id].paths + issue = new_dom.createElement('issue') + issue.attributes['id'] = issue_id + if severity: + issue.attributes['severity'] = severity + if severity == 'ignore': + print 'Warning: [%s] is suppressed globally.' % issue_id + else: + for path in sorted(paths): + ignore = new_dom.createElement('ignore') + ignore.attributes['path'] = path + issue.appendChild(ignore) + top_element.appendChild(issue) + + with open(config_path, 'w') as f: + f.write(new_dom.toprettyxml(indent=' ', encoding='utf-8')) + print 'Updated %s' % config_path + + +def _Suppress(config_path, result_path): + issues_dict = _ParseConfigFile(config_path) + _ParseAndMergeResultFile(result_path, issues_dict) + _WriteConfigFile(config_path, issues_dict) + + +def main(): + parser = optparse.OptionParser(usage='%prog RESULT-FILE') + _, args = parser.parse_args() + + if len(args) != 1 or not os.path.exists(args[0]): + parser.error('Must provide RESULT-FILE') + + _Suppress(_CONFIG_PATH, args[0]) + + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/build/android/lint/suppressions.xml b/engine/src/flutter/build/android/lint/suppressions.xml new file mode 100644 index 0000000000..6921695e6a --- /dev/null +++ b/engine/src/flutter/build/android/lint/suppressions.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/src/flutter/build/android/ndk.gyp b/engine/src/flutter/build/android/ndk.gyp new file mode 100644 index 0000000000..2838a9857e --- /dev/null +++ b/engine/src/flutter/build/android/ndk.gyp @@ -0,0 +1,20 @@ +# Copyright (c) 2015 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. + +{ + 'targets': [ + { + 'target_name': 'cpu_features', + 'type': 'static_library', + 'direct_dependent_settings': { + 'include_dirs': [ + '<(android_ndk_root)/sources/android/cpufeatures', + ], + }, + 'sources': [ + '<(android_ndk_root)/sources/android/cpufeatures/cpu-features.c', + ], + }, + ], +} diff --git a/engine/src/flutter/build/android/provision_devices.py b/engine/src/flutter/build/android/provision_devices.py new file mode 100755 index 0000000000..259bd55ab2 --- /dev/null +++ b/engine/src/flutter/build/android/provision_devices.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python +# +# 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. + +"""Provisions Android devices with settings required for bots. + +Usage: + ./provision_devices.py [-d ] +""" + +import argparse +import logging +import os +import posixpath +import re +import subprocess +import sys +import time + +from pylib import constants +from pylib import device_settings +from pylib.device import battery_utils +from pylib.device import device_blacklist +from pylib.device import device_errors +from pylib.device import device_utils +from pylib.utils import run_tests_helper +from pylib.utils import timeout_retry + +sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, + 'third_party', 'android_testrunner')) +import errors + + +class _DEFAULT_TIMEOUTS(object): + # L can take a while to reboot after a wipe. + LOLLIPOP = 600 + PRE_LOLLIPOP = 180 + + HELP_TEXT = '{}s on L, {}s on pre-L'.format(LOLLIPOP, PRE_LOLLIPOP) + + +class _PHASES(object): + WIPE = 'wipe' + PROPERTIES = 'properties' + FINISH = 'finish' + + ALL = [WIPE, PROPERTIES, FINISH] + + +def ProvisionDevices(options): + devices = device_utils.DeviceUtils.HealthyDevices() + if options.device: + devices = [d for d in devices if d == options.device] + if not devices: + raise device_errors.DeviceUnreachableError(options.device) + + parallel_devices = device_utils.DeviceUtils.parallel(devices) + parallel_devices.pMap(ProvisionDevice, options) + if options.auto_reconnect: + _LaunchHostHeartbeat() + blacklist = device_blacklist.ReadBlacklist() + if all(d in blacklist for d in devices): + raise device_errors.NoDevicesError + return 0 + + +def ProvisionDevice(device, options): + if options.reboot_timeout: + reboot_timeout = options.reboot_timeout + elif (device.build_version_sdk >= + constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP): + reboot_timeout = _DEFAULT_TIMEOUTS.LOLLIPOP + else: + reboot_timeout = _DEFAULT_TIMEOUTS.PRE_LOLLIPOP + + def should_run_phase(phase_name): + return not options.phases or phase_name in options.phases + + def run_phase(phase_func, reboot=True): + device.WaitUntilFullyBooted(timeout=reboot_timeout) + phase_func(device, options) + if reboot: + device.Reboot(False, retries=0) + device.adb.WaitForDevice() + + try: + if should_run_phase(_PHASES.WIPE): + run_phase(WipeDevice) + + if should_run_phase(_PHASES.PROPERTIES): + run_phase(SetProperties) + + if should_run_phase(_PHASES.FINISH): + run_phase(FinishProvisioning, reboot=False) + + except (errors.WaitForResponseTimedOutError, + device_errors.CommandTimeoutError): + logging.exception('Timed out waiting for device %s. Adding to blacklist.', + str(device)) + device_blacklist.ExtendBlacklist([str(device)]) + + except device_errors.CommandFailedError: + logging.exception('Failed to provision device %s. Adding to blacklist.', + str(device)) + device_blacklist.ExtendBlacklist([str(device)]) + + +def WipeDevice(device, options): + """Wipes data from device, keeping only the adb_keys for authorization. + + After wiping data on a device that has been authorized, adb can still + communicate with the device, but after reboot the device will need to be + re-authorized because the adb keys file is stored in /data/misc/adb/. + Thus, adb_keys file is rewritten so the device does not need to be + re-authorized. + + Arguments: + device: the device to wipe + """ + if options.skip_wipe: + return + + try: + device.EnableRoot() + device_authorized = device.FileExists(constants.ADB_KEYS_FILE) + if device_authorized: + adb_keys = device.ReadFile(constants.ADB_KEYS_FILE, + as_root=True).splitlines() + device.RunShellCommand(['wipe', 'data'], + as_root=True, check_return=True) + device.adb.WaitForDevice() + + if device_authorized: + adb_keys_set = set(adb_keys) + for adb_key_file in options.adb_key_files or []: + try: + with open(adb_key_file, 'r') as f: + adb_public_keys = f.readlines() + adb_keys_set.update(adb_public_keys) + except IOError: + logging.warning('Unable to find adb keys file %s.' % adb_key_file) + _WriteAdbKeysFile(device, '\n'.join(adb_keys_set)) + except device_errors.CommandFailedError: + logging.exception('Possible failure while wiping the device. ' + 'Attempting to continue.') + + +def _WriteAdbKeysFile(device, adb_keys_string): + dir_path = posixpath.dirname(constants.ADB_KEYS_FILE) + device.RunShellCommand(['mkdir', '-p', dir_path], + as_root=True, check_return=True) + device.RunShellCommand(['restorecon', dir_path], + as_root=True, check_return=True) + device.WriteFile(constants.ADB_KEYS_FILE, adb_keys_string, as_root=True) + device.RunShellCommand(['restorecon', constants.ADB_KEYS_FILE], + as_root=True, check_return=True) + + +def SetProperties(device, options): + try: + device.EnableRoot() + except device_errors.CommandFailedError as e: + logging.warning(str(e)) + + _ConfigureLocalProperties(device, options.enable_java_debug) + device_settings.ConfigureContentSettings( + device, device_settings.DETERMINISTIC_DEVICE_SETTINGS) + if options.disable_location: + device_settings.ConfigureContentSettings( + device, device_settings.DISABLE_LOCATION_SETTINGS) + else: + device_settings.ConfigureContentSettings( + device, device_settings.ENABLE_LOCATION_SETTINGS) + device_settings.SetLockScreenSettings(device) + if options.disable_network: + device_settings.ConfigureContentSettings( + device, device_settings.NETWORK_DISABLED_SETTINGS) + + if options.min_battery_level is not None: + try: + battery = battery_utils.BatteryUtils(device) + battery.ChargeDeviceToLevel(options.min_battery_level) + except device_errors.CommandFailedError as e: + logging.exception('Unable to charge device to specified level.') + + if options.max_battery_temp is not None: + try: + battery = battery_utils.BatteryUtils(device) + battery.LetBatteryCoolToTemperature(options.max_battery_temp) + except device_errors.CommandFailedError as e: + logging.exception('Unable to let battery cool to specified temperature.') + +def _ConfigureLocalProperties(device, java_debug=True): + """Set standard readonly testing device properties prior to reboot.""" + local_props = [ + 'persist.sys.usb.config=adb', + 'ro.monkey=1', + 'ro.test_harness=1', + 'ro.audio.silent=1', + 'ro.setupwizard.mode=DISABLED', + ] + if java_debug: + local_props.append( + '%s=all' % device_utils.DeviceUtils.JAVA_ASSERT_PROPERTY) + local_props.append('debug.checkjni=1') + try: + device.WriteFile( + constants.DEVICE_LOCAL_PROPERTIES_PATH, + '\n'.join(local_props), as_root=True) + # Android will not respect the local props file if it is world writable. + device.RunShellCommand( + ['chmod', '644', constants.DEVICE_LOCAL_PROPERTIES_PATH], + as_root=True, check_return=True) + except device_errors.CommandFailedError: + logging.exception('Failed to configure local properties.') + + +def FinishProvisioning(device, options): + device.RunShellCommand( + ['date', '-s', time.strftime('%Y%m%d.%H%M%S', time.gmtime())], + as_root=True, check_return=True) + props = device.RunShellCommand('getprop', check_return=True) + for prop in props: + logging.info(' %s' % prop) + if options.auto_reconnect: + _PushAndLaunchAdbReboot(device, options.target) + + +def _PushAndLaunchAdbReboot(device, target): + """Pushes and launches the adb_reboot binary on the device. + + Arguments: + device: The DeviceUtils instance for the device to which the adb_reboot + binary should be pushed. + target: The build target (example, Debug or Release) which helps in + locating the adb_reboot binary. + """ + logging.info('Will push and launch adb_reboot on %s' % str(device)) + # Kill if adb_reboot is already running. + device.KillAll('adb_reboot', blocking=True, timeout=2, quiet=True) + # Push adb_reboot + logging.info(' Pushing adb_reboot ...') + adb_reboot = os.path.join(constants.DIR_SOURCE_ROOT, + 'out/%s/adb_reboot' % target) + device.PushChangedFiles([(adb_reboot, '/data/local/tmp/')]) + # Launch adb_reboot + logging.info(' Launching adb_reboot ...') + device.RunShellCommand( + ['/data/local/tmp/adb_reboot'], + check_return=True) + + +def _LaunchHostHeartbeat(): + # Kill if existing host_heartbeat + KillHostHeartbeat() + # Launch a new host_heartbeat + logging.info('Spawning host heartbeat...') + subprocess.Popen([os.path.join(constants.DIR_SOURCE_ROOT, + 'build/android/host_heartbeat.py')]) + + +def KillHostHeartbeat(): + ps = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE) + stdout, _ = ps.communicate() + matches = re.findall('\\n.*host_heartbeat.*', stdout) + for match in matches: + logging.info('An instance of host heart beart running... will kill') + pid = re.findall(r'(\S+)', match)[1] + subprocess.call(['kill', str(pid)]) + + +def main(): + # Recommended options on perf bots: + # --disable-network + # TODO(tonyg): We eventually want network on. However, currently radios + # can cause perfbots to drain faster than they charge. + # --min-battery-level 95 + # Some perf bots run benchmarks with USB charging disabled which leads + # to gradual draining of the battery. We must wait for a full charge + # before starting a run in order to keep the devices online. + + parser = argparse.ArgumentParser( + description='Provision Android devices with settings required for bots.') + parser.add_argument('-d', '--device', metavar='SERIAL', + help='the serial number of the device to be provisioned' + ' (the default is to provision all devices attached)') + parser.add_argument('--phase', action='append', choices=_PHASES.ALL, + dest='phases', + help='Phases of provisioning to run. ' + '(If omitted, all phases will be run.)') + parser.add_argument('--skip-wipe', action='store_true', default=False, + help="don't wipe device data during provisioning") + parser.add_argument('--reboot-timeout', metavar='SECS', type=int, + help='when wiping the device, max number of seconds to' + ' wait after each reboot ' + '(default: %s)' % _DEFAULT_TIMEOUTS.HELP_TEXT) + parser.add_argument('--min-battery-level', type=int, metavar='NUM', + help='wait for the device to reach this minimum battery' + ' level before trying to continue') + parser.add_argument('--disable-location', action='store_true', + help='disable Google location services on devices') + parser.add_argument('--disable-network', action='store_true', + help='disable network access on devices') + parser.add_argument('--disable-java-debug', action='store_false', + dest='enable_java_debug', default=True, + help='disable Java property asserts and JNI checking') + parser.add_argument('-t', '--target', default='Debug', + help='the build target (default: %(default)s)') + parser.add_argument('-r', '--auto-reconnect', action='store_true', + help='push binary which will reboot the device on adb' + ' disconnections') + parser.add_argument('--adb-key-files', type=str, nargs='+', + help='list of adb keys to push to device') + parser.add_argument('-v', '--verbose', action='count', default=1, + help='Log more information.') + parser.add_argument('--max-battery-temp', type=int, metavar='NUM', + help='Wait for the battery to have this temp or lower.') + args = parser.parse_args() + constants.SetBuildType(args.target) + + run_tests_helper.SetLogLevel(args.verbose) + + return ProvisionDevices(args) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/android/pylib/OWNERS b/engine/src/flutter/build/android/pylib/OWNERS new file mode 100644 index 0000000000..dbbbba7f27 --- /dev/null +++ b/engine/src/flutter/build/android/pylib/OWNERS @@ -0,0 +1,4 @@ +jbudorick@chromium.org +klundberg@chromium.org +navabi@chromium.org +skyostil@chromium.org diff --git a/engine/src/flutter/build/android/pylib/__init__.py b/engine/src/flutter/build/android/pylib/__init__.py new file mode 100644 index 0000000000..96196cffb2 --- /dev/null +++ b/engine/src/flutter/build/android/pylib/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) 2012 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. diff --git a/engine/src/flutter/build/android/pylib/android_commands.py b/engine/src/flutter/build/android/pylib/android_commands.py new file mode 100644 index 0000000000..f7191f7935 --- /dev/null +++ b/engine/src/flutter/build/android/pylib/android_commands.py @@ -0,0 +1,1976 @@ +# Copyright (c) 2012 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. + +"""Provides an interface to communicate with the device via the adb command. + +Assumes adb binary is currently on system path. + +Note that this module is deprecated. +""" +# TODO(jbudorick): Delete this file once no clients use it. + +# pylint: skip-file + +import collections +import datetime +import inspect +import logging +import os +import random +import re +import shlex +import signal +import subprocess +import sys +import tempfile +import time + +import cmd_helper +import constants +import system_properties +from utils import host_utils + +try: + from pylib import pexpect +except ImportError: + pexpect = None + +sys.path.append(os.path.join( + constants.DIR_SOURCE_ROOT, 'third_party', 'android_testrunner')) +import adb_interface +import am_instrument_parser +import errors + +from pylib.device import device_blacklist +from pylib.device import device_errors + +# Pattern to search for the next whole line of pexpect output and capture it +# into a match group. We can't use ^ and $ for line start end with pexpect, +# see http://www.noah.org/python/pexpect/#doc for explanation why. +PEXPECT_LINE_RE = re.compile('\n([^\r]*)\r') + +# Set the adb shell prompt to be a unique marker that will [hopefully] not +# appear at the start of any line of a command's output. +SHELL_PROMPT = '~+~PQ\x17RS~+~' + +# Java properties file +LOCAL_PROPERTIES_PATH = constants.DEVICE_LOCAL_PROPERTIES_PATH + +# Property in /data/local.prop that controls Java assertions. +JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' + +# Keycode "enum" suitable for passing to AndroidCommands.SendKey(). +KEYCODE_HOME = 3 +KEYCODE_BACK = 4 +KEYCODE_DPAD_UP = 19 +KEYCODE_DPAD_DOWN = 20 +KEYCODE_DPAD_RIGHT = 22 +KEYCODE_ENTER = 66 +KEYCODE_MENU = 82 + +MD5SUM_DEVICE_FOLDER = constants.TEST_EXECUTABLE_DIR + '/md5sum/' +MD5SUM_DEVICE_PATH = MD5SUM_DEVICE_FOLDER + 'md5sum_bin' + +PIE_WRAPPER_PATH = constants.TEST_EXECUTABLE_DIR + '/run_pie' + +CONTROL_USB_CHARGING_COMMANDS = [ + { + # Nexus 4 + 'witness_file': '/sys/module/pm8921_charger/parameters/disabled', + 'enable_command': 'echo 0 > /sys/module/pm8921_charger/parameters/disabled', + 'disable_command': + 'echo 1 > /sys/module/pm8921_charger/parameters/disabled', + }, + { + # Nexus 5 + # Setting the HIZ bit of the bq24192 causes the charger to actually ignore + # energy coming from USB. Setting the power_supply offline just updates the + # Android system to reflect that. + 'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT', + 'enable_command': ( + 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' + 'echo 1 > /sys/class/power_supply/usb/online'), + 'disable_command': ( + 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' + 'chmod 644 /sys/class/power_supply/usb/online && ' + 'echo 0 > /sys/class/power_supply/usb/online'), + }, +] + +class DeviceTempFile(object): + def __init__(self, android_commands, prefix='temp_file', suffix=''): + """Find an unused temporary file path in the devices external directory. + + When this object is closed, the file will be deleted on the device. + """ + self.android_commands = android_commands + while True: + # TODO(cjhopman): This could actually return the same file in multiple + # calls if the caller doesn't write to the files immediately. This is + # expected to never happen. + i = random.randint(0, 1000000) + self.name = '%s/%s-%d-%010d%s' % ( + android_commands.GetExternalStorage(), + prefix, int(time.time()), i, suffix) + if not android_commands.FileExistsOnDevice(self.name): + break + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def close(self): + self.android_commands.RunShellCommand('rm ' + self.name) + + +def GetAVDs(): + """Returns a list of AVDs.""" + re_avd = re.compile('^[ ]+Name: ([a-zA-Z0-9_:.-]+)', re.MULTILINE) + avds = re_avd.findall(cmd_helper.GetCmdOutput(['android', 'list', 'avd'])) + return avds + +def ResetBadDevices(): + """Removes the blacklist that keeps track of bad devices for a current + build. + """ + device_blacklist.ResetBlacklist() + +def ExtendBadDevices(devices): + """Adds devices to the blacklist that keeps track of bad devices for a + current build. + + The devices listed in the bad devices file will not be returned by + GetAttachedDevices. + + Args: + devices: list of bad devices to be added to the bad devices file. + """ + device_blacklist.ExtendBlacklist(devices) + + +def GetAttachedDevices(hardware=True, emulator=True, offline=False): + """Returns a list of attached, android devices and emulators. + + If a preferred device has been set with ANDROID_SERIAL, it will be first in + the returned list. The arguments specify what devices to include in the list. + + Example output: + + * daemon not running. starting it now on port 5037 * + * daemon started successfully * + List of devices attached + 027c10494100b4d7 device + emulator-5554 offline + + Args: + hardware: Include attached actual devices that are online. + emulator: Include emulators (i.e. AVD's) currently on host. + offline: Include devices and emulators that are offline. + + Returns: List of devices. + """ + adb_devices_output = cmd_helper.GetCmdOutput([constants.GetAdbPath(), + 'devices']) + + re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE) + online_devices = re_device.findall(adb_devices_output) + + re_device = re.compile('^(emulator-[0-9]+)\tdevice', re.MULTILINE) + emulator_devices = re_device.findall(adb_devices_output) + + re_device = re.compile('^([a-zA-Z0-9_:.-]+)\t(?:offline|unauthorized)$', + re.MULTILINE) + offline_devices = re_device.findall(adb_devices_output) + + devices = [] + # First determine list of online devices (e.g. hardware and/or emulator). + if hardware and emulator: + devices = online_devices + elif hardware: + devices = [device for device in online_devices + if device not in emulator_devices] + elif emulator: + devices = emulator_devices + + # Now add offline devices if offline is true + if offline: + devices = devices + offline_devices + + # Remove any devices in the blacklist. + blacklist = device_blacklist.ReadBlacklist() + if len(blacklist): + logging.info('Avoiding bad devices %s', ' '.join(blacklist)) + devices = [device for device in devices if device not in blacklist] + + preferred_device = os.environ.get('ANDROID_SERIAL') + if preferred_device in devices: + devices.remove(preferred_device) + devices.insert(0, preferred_device) + return devices + + +def IsDeviceAttached(device): + """Return true if the device is attached and online.""" + return device in GetAttachedDevices() + + +def _GetFilesFromRecursiveLsOutput(path, ls_output, re_file, utc_offset=None): + """Gets a list of files from `ls` command output. + + Python's os.walk isn't used because it doesn't work over adb shell. + + Args: + path: The path to list. + ls_output: A list of lines returned by an `ls -lR` command. + re_file: A compiled regular expression which parses a line into named groups + consisting of at minimum "filename", "date", "time", "size" and + optionally "timezone". + utc_offset: A 5-character string of the form +HHMM or -HHMM, where HH is a + 2-digit string giving the number of UTC offset hours, and MM is a + 2-digit string giving the number of UTC offset minutes. If the input + utc_offset is None, will try to look for the value of "timezone" if it + is specified in re_file. + + Returns: + A dict of {"name": (size, lastmod), ...} where: + name: The file name relative to |path|'s directory. + size: The file size in bytes (0 for directories). + lastmod: The file last modification date in UTC. + """ + re_directory = re.compile('^%s/(?P[^:]+):$' % re.escape(path)) + path_dir = os.path.dirname(path) + + current_dir = '' + files = {} + for line in ls_output: + directory_match = re_directory.match(line) + if directory_match: + current_dir = directory_match.group('dir') + continue + file_match = re_file.match(line) + if file_match: + filename = os.path.join(current_dir, file_match.group('filename')) + if filename.startswith(path_dir): + filename = filename[len(path_dir) + 1:] + lastmod = datetime.datetime.strptime( + file_match.group('date') + ' ' + file_match.group('time')[:5], + '%Y-%m-%d %H:%M') + if not utc_offset and 'timezone' in re_file.groupindex: + utc_offset = file_match.group('timezone') + if isinstance(utc_offset, str) and len(utc_offset) == 5: + utc_delta = datetime.timedelta(hours=int(utc_offset[1:3]), + minutes=int(utc_offset[3:5])) + if utc_offset[0:1] == '-': + utc_delta = -utc_delta + lastmod -= utc_delta + files[filename] = (int(file_match.group('size')), lastmod) + return files + + +def _ParseMd5SumOutput(md5sum_output): + """Returns a list of tuples from the provided md5sum output. + + Args: + md5sum_output: output directly from md5sum binary. + + Returns: + List of namedtuples with attributes |hash| and |path|, where |path| is the + absolute path to the file with an Md5Sum of |hash|. + """ + HashAndPath = collections.namedtuple('HashAndPath', ['hash', 'path']) + split_lines = [line.split(' ') for line in md5sum_output] + return [HashAndPath._make(s) for s in split_lines if len(s) == 2] + + +def _HasAdbPushSucceeded(command_output): + """Returns whether adb push has succeeded from the provided output.""" + # TODO(frankf): We should look at the return code instead of the command + # output for many of the commands in this file. + if not command_output: + return True + # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)" + # Errors look like this: "failed to copy ... " + if not re.search('^[0-9]', command_output.splitlines()[-1]): + logging.critical('PUSH FAILED: ' + command_output) + return False + return True + + +def GetLogTimestamp(log_line, year): + """Returns the timestamp of the given |log_line| in the given year.""" + try: + return datetime.datetime.strptime('%s-%s' % (year, log_line[:18]), + '%Y-%m-%d %H:%M:%S.%f') + except (ValueError, IndexError): + logging.critical('Error reading timestamp from ' + log_line) + return None + + +class AndroidCommands(object): + """Helper class for communicating with Android device via adb.""" + + def __init__(self, device=None): + """Constructor. + + Args: + device: If given, adb commands are only send to the device of this ID. + Otherwise commands are sent to all attached devices. + """ + self._adb = adb_interface.AdbInterface(constants.GetAdbPath()) + if device: + self._adb.SetTargetSerial(device) + self._device = device + self._logcat = None + self.logcat_process = None + self._logcat_tmpoutfile = None + self._pushed_files = [] + self._device_utc_offset = None + self._potential_push_size = 0 + self._actual_push_size = 0 + self._external_storage = '' + self._util_wrapper = '' + self._system_properties = system_properties.SystemProperties(self.Adb()) + self._push_if_needed_cache = {} + self._control_usb_charging_command = { + 'command': None, + 'cached': False, + } + self._protected_file_access_method_initialized = None + self._privileged_command_runner = None + self._pie_wrapper = None + + @property + def system_properties(self): + return self._system_properties + + def _LogShell(self, cmd): + """Logs the adb shell command.""" + if self._device: + device_repr = self._device[-4:] + else: + device_repr = '????' + logging.info('[%s]> %s', device_repr, cmd) + + def Adb(self): + """Returns our AdbInterface to avoid us wrapping all its methods.""" + # TODO(tonyg): Goal should be to git rid of this method by making this API + # complete and alleviating the need. + return self._adb + + def GetDevice(self): + """Returns the device serial.""" + return self._device + + def IsOnline(self): + """Checks whether the device is online. + + Returns: + True if device is in 'device' mode, False otherwise. + """ + # TODO(aurimas): revert to using adb get-state when android L adb is fixed. + #out = self._adb.SendCommand('get-state') + #return out.strip() == 'device' + + out = self._adb.SendCommand('devices') + for line in out.split('\n'): + if self._device in line and 'device' in line: + return True + return False + + def IsRootEnabled(self): + """Checks if root is enabled on the device.""" + root_test_output = self.RunShellCommand('ls /root') or [''] + return not 'Permission denied' in root_test_output[0] + + def EnableAdbRoot(self): + """Enables adb root on the device. + + Returns: + True: if output from executing adb root was as expected. + False: otherwise. + """ + if self.GetBuildType() == 'user': + logging.warning("Can't enable root in production builds with type user") + return False + else: + return_value = self._adb.EnableAdbRoot() + # EnableAdbRoot inserts a call for wait-for-device only when adb logcat + # output matches what is expected. Just to be safe add a call to + # wait-for-device. + self._adb.SendCommand('wait-for-device') + return return_value + + def GetDeviceYear(self): + """Returns the year information of the date on device.""" + return self.RunShellCommand('date +%Y')[0] + + def GetExternalStorage(self): + if not self._external_storage: + self._external_storage = self.RunShellCommand('echo $EXTERNAL_STORAGE')[0] + if not self._external_storage: + raise device_errors.CommandFailedError( + ['shell', "'echo $EXTERNAL_STORAGE'"], + 'Unable to find $EXTERNAL_STORAGE') + return self._external_storage + + def WaitForDevicePm(self, timeout=120): + """Blocks until the device's package manager is available. + + To workaround http://b/5201039, we restart the shell and retry if the + package manager isn't back after 120 seconds. + + Raises: + errors.WaitForResponseTimedOutError after max retries reached. + """ + last_err = None + retries = 3 + while retries: + try: + self._adb.WaitForDevicePm(wait_time=timeout) + return # Success + except errors.WaitForResponseTimedOutError as e: + last_err = e + logging.warning('Restarting and retrying after timeout: %s', e) + retries -= 1 + self.RestartShell() + raise last_err # Only reached after max retries, re-raise the last error. + + def RestartShell(self): + """Restarts the shell on the device. Does not block for it to return.""" + self.RunShellCommand('stop') + self.RunShellCommand('start') + + def Reboot(self, full_reboot=True): + """Reboots the device and waits for the package manager to return. + + Args: + full_reboot: Whether to fully reboot the device or just restart the shell. + """ + # TODO(torne): hive can't reboot the device either way without breaking the + # connection; work out if we can handle this better + if os.environ.get('USING_HIVE'): + logging.warning('Ignoring reboot request as we are on hive') + return + if full_reboot or not self.IsRootEnabled(): + self._adb.SendCommand('reboot') + self._system_properties = system_properties.SystemProperties(self.Adb()) + timeout = 300 + retries = 1 + # Wait for the device to disappear. + while retries < 10 and self.IsOnline(): + time.sleep(1) + retries += 1 + else: + self.RestartShell() + timeout = 120 + # To run tests we need at least the package manager and the sd card (or + # other external storage) to be ready. + self.WaitForDevicePm(timeout) + self.WaitForSdCardReady(timeout) + + def Shutdown(self): + """Shuts down the device.""" + self._adb.SendCommand('reboot -p') + self._system_properties = system_properties.SystemProperties(self.Adb()) + + def Uninstall(self, package): + """Uninstalls the specified package from the device. + + Args: + package: Name of the package to remove. + + Returns: + A status string returned by adb uninstall + """ + uninstall_command = 'uninstall %s' % package + + self._LogShell(uninstall_command) + return self._adb.SendCommand(uninstall_command, timeout_time=60) + + def Install(self, package_file_path, reinstall=False): + """Installs the specified package to the device. + + Args: + package_file_path: Path to .apk file to install. + reinstall: Reinstall an existing apk, keeping the data. + + Returns: + A status string returned by adb install + """ + assert os.path.isfile(package_file_path), ('<%s> is not file' % + package_file_path) + + install_cmd = ['install'] + + if reinstall: + install_cmd.append('-r') + + install_cmd.append(package_file_path) + install_cmd = ' '.join(install_cmd) + + self._LogShell(install_cmd) + return self._adb.SendCommand(install_cmd, + timeout_time=2 * 60, + retry_count=0) + + def ManagedInstall(self, apk_path, keep_data=False, package_name=None, + reboots_on_timeout=2): + """Installs specified package and reboots device on timeouts. + + If package_name is supplied, checks if the package is already installed and + doesn't reinstall if the apk md5sums match. + + Args: + apk_path: Path to .apk file to install. + keep_data: Reinstalls instead of uninstalling first, preserving the + application data. + package_name: Package name (only needed if keep_data=False). + reboots_on_timeout: number of time to reboot if package manager is frozen. + """ + # Check if package is already installed and up to date. + if package_name: + installed_apk_path = self.GetApplicationPath(package_name) + if (installed_apk_path and + not self.GetFilesChanged(apk_path, installed_apk_path, + ignore_filenames=True)): + logging.info('Skipped install: identical %s APK already installed' % + package_name) + return + # Install. + reboots_left = reboots_on_timeout + while True: + try: + if not keep_data: + assert package_name + self.Uninstall(package_name) + install_status = self.Install(apk_path, reinstall=keep_data) + if 'Success' in install_status: + return + else: + raise Exception('Install failure: %s' % install_status) + except errors.WaitForResponseTimedOutError: + print '@@@STEP_WARNINGS@@@' + logging.info('Timeout on installing %s on device %s', apk_path, + self._device) + + if reboots_left <= 0: + raise Exception('Install timed out') + + # Force a hard reboot on last attempt + self.Reboot(full_reboot=(reboots_left == 1)) + reboots_left -= 1 + + def MakeSystemFolderWritable(self): + """Remounts the /system folder rw.""" + out = self._adb.SendCommand('remount') + if out.strip() != 'remount succeeded': + raise errors.MsgException('Remount failed: %s' % out) + + def RestartAdbdOnDevice(self): + logging.info('Restarting adbd on the device...') + with DeviceTempFile(self, suffix=".sh") as temp_script_file: + host_script_path = os.path.join(constants.DIR_SOURCE_ROOT, + 'build', + 'android', + 'pylib', + 'restart_adbd.sh') + self._adb.Push(host_script_path, temp_script_file.name) + self.RunShellCommand('. %s' % temp_script_file.name) + self._adb.SendCommand('wait-for-device') + + def RestartAdbServer(self): + """Restart the adb server.""" + ret = self.KillAdbServer() + if ret != 0: + raise errors.MsgException('KillAdbServer: %d' % ret) + + ret = self.StartAdbServer() + if ret != 0: + raise errors.MsgException('StartAdbServer: %d' % ret) + + @staticmethod + def KillAdbServer(): + """Kill adb server.""" + adb_cmd = [constants.GetAdbPath(), 'kill-server'] + ret = cmd_helper.RunCmd(adb_cmd) + retry = 0 + while retry < 3: + ret, _ = cmd_helper.GetCmdStatusAndOutput(['pgrep', 'adb']) + if ret != 0: + # pgrep didn't find adb, kill-server succeeded. + return 0 + retry += 1 + time.sleep(retry) + return ret + + def StartAdbServer(self): + """Start adb server.""" + adb_cmd = ['taskset', '-c', '0', constants.GetAdbPath(), 'start-server'] + ret, _ = cmd_helper.GetCmdStatusAndOutput(adb_cmd) + retry = 0 + while retry < 3: + ret, _ = cmd_helper.GetCmdStatusAndOutput(['pgrep', 'adb']) + if ret == 0: + # pgrep found adb, start-server succeeded. + # Waiting for device to reconnect before returning success. + self._adb.SendCommand('wait-for-device') + return 0 + retry += 1 + time.sleep(retry) + return ret + + def WaitForSystemBootCompleted(self, wait_time): + """Waits for targeted system's boot_completed flag to be set. + + Args: + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and flag still not + set. + """ + logging.info('Waiting for system boot completed...') + self._adb.SendCommand('wait-for-device') + # Now the device is there, but system not boot completed. + # Query the sys.boot_completed flag with a basic command + boot_completed = False + attempts = 0 + wait_period = 5 + while not boot_completed and (attempts * wait_period) < wait_time: + output = self.system_properties['sys.boot_completed'] + output = output.strip() + if output == '1': + boot_completed = True + else: + # If 'error: xxx' returned when querying the flag, it means + # adb server lost the connection to the emulator, so restart the adb + # server. + if 'error:' in output: + self.RestartAdbServer() + time.sleep(wait_period) + attempts += 1 + if not boot_completed: + raise errors.WaitForResponseTimedOutError( + 'sys.boot_completed flag was not set after %s seconds' % wait_time) + + def WaitForSdCardReady(self, timeout_time): + """Wait for the SD card ready before pushing data into it.""" + logging.info('Waiting for SD card ready...') + sdcard_ready = False + attempts = 0 + wait_period = 5 + external_storage = self.GetExternalStorage() + while not sdcard_ready and attempts * wait_period < timeout_time: + output = self.RunShellCommand('ls ' + external_storage) + if output: + sdcard_ready = True + else: + time.sleep(wait_period) + attempts += 1 + if not sdcard_ready: + raise errors.WaitForResponseTimedOutError( + 'SD card not ready after %s seconds' % timeout_time) + + def GetAndroidToolStatusAndOutput(self, command, lib_path=None, *args, **kw): + """Runs a native Android binary, wrapping the command as necessary. + + This is a specialization of GetShellCommandStatusAndOutput, which is meant + for running tools/android/ binaries and handle properly: (1) setting the + lib path (for component=shared_library), (2) using the PIE wrapper on ICS. + See crbug.com/373219 for more context. + + Args: + command: String containing the command to send. + lib_path: (optional) path to the folder containing the dependent libs. + Same other arguments of GetCmdStatusAndOutput. + """ + # The first time this command is run the device is inspected to check + # whether a wrapper for running PIE executable is needed (only Android ICS) + # or not. The results is cached, so the wrapper is pushed only once. + if self._pie_wrapper is None: + # None: did not check; '': did check and not needed; '/path': use /path. + self._pie_wrapper = '' + if self.GetBuildId().startswith('I'): # Ixxxx = Android ICS. + run_pie_dist_path = os.path.join(constants.GetOutDirectory(), 'run_pie') + assert os.path.exists(run_pie_dist_path), 'Please build run_pie' + # The PIE loader must be pushed manually (i.e. no PushIfNeeded) because + # PushIfNeeded requires md5sum and md5sum requires the wrapper as well. + adb_command = 'push %s %s' % (run_pie_dist_path, PIE_WRAPPER_PATH) + assert _HasAdbPushSucceeded(self._adb.SendCommand(adb_command)) + self._pie_wrapper = PIE_WRAPPER_PATH + + if self._pie_wrapper: + command = '%s %s' % (self._pie_wrapper, command) + if lib_path: + command = 'LD_LIBRARY_PATH=%s %s' % (lib_path, command) + return self.GetShellCommandStatusAndOutput(command, *args, **kw) + + # It is tempting to turn this function into a generator, however this is not + # possible without using a private (local) adb_shell instance (to ensure no + # other command interleaves usage of it), which would defeat the main aim of + # being able to reuse the adb shell instance across commands. + def RunShellCommand(self, command, timeout_time=20, log_result=False): + """Send a command to the adb shell and return the result. + + Args: + command: String containing the shell command to send. + timeout_time: Number of seconds to wait for command to respond before + retrying, used by AdbInterface.SendShellCommand. + log_result: Boolean to indicate whether we should log the result of the + shell command. + + Returns: + list containing the lines of output received from running the command + """ + self._LogShell(command) + if "'" in command: + command = command.replace('\'', '\'\\\'\'') + result = self._adb.SendShellCommand( + "'%s'" % command, timeout_time).splitlines() + # TODO(b.kelemen): we should really be able to drop the stderr of the + # command or raise an exception based on what the caller wants. + result = [ l for l in result if not l.startswith('WARNING') ] + if ['error: device not found'] == result: + raise errors.DeviceUnresponsiveError('device not found') + if log_result: + self._LogShell('\n'.join(result)) + return result + + def GetShellCommandStatusAndOutput(self, command, timeout_time=20, + log_result=False): + """See RunShellCommand() above. + + Returns: + The tuple (exit code, list of output lines). + """ + lines = self.RunShellCommand( + command + '; echo %$?', timeout_time, log_result) + last_line = lines[-1] + status_pos = last_line.rfind('%') + assert status_pos >= 0 + status = int(last_line[status_pos + 1:]) + if status_pos == 0: + lines = lines[:-1] + else: + lines = lines[:-1] + [last_line[:status_pos]] + return (status, lines) + + def KillAll(self, process, signum=9, with_su=False): + """Android version of killall, connected via adb. + + Args: + process: name of the process to kill off. + signum: signal to use, 9 (SIGKILL) by default. + with_su: wether or not to use su to kill the processes. + + Returns: + the number of processes killed + """ + pids = self.ExtractPid(process) + if pids: + cmd = 'kill -%d %s' % (signum, ' '.join(pids)) + if with_su: + self.RunShellCommandWithSU(cmd) + else: + self.RunShellCommand(cmd) + return len(pids) + + def KillAllBlocking(self, process, timeout_sec, signum=9, with_su=False): + """Blocking version of killall, connected via adb. + + This waits until no process matching the corresponding name appears in ps' + output anymore. + + Args: + process: name of the process to kill off + timeout_sec: the timeout in seconds + signum: same as |KillAll| + with_su: same as |KillAll| + Returns: + the number of processes killed + """ + processes_killed = self.KillAll(process, signum=signum, with_su=with_su) + if processes_killed: + elapsed = 0 + wait_period = 0.1 + # Note that this doesn't take into account the time spent in ExtractPid(). + while self.ExtractPid(process) and elapsed < timeout_sec: + time.sleep(wait_period) + elapsed += wait_period + if elapsed >= timeout_sec: + return processes_killed - self.ExtractPid(process) + return processes_killed + + @staticmethod + def _GetActivityCommand(package, activity, wait_for_completion, action, + category, data, extras, trace_file_name, force_stop, + flags): + """Creates command to start |package|'s activity on the device. + + Args - as for StartActivity + + Returns: + the command to run on the target to start the activity + """ + cmd = 'am start -a %s' % action + if force_stop: + cmd += ' -S' + if wait_for_completion: + cmd += ' -W' + if category: + cmd += ' -c %s' % category + if package and activity: + cmd += ' -n %s/%s' % (package, activity) + if data: + cmd += ' -d "%s"' % data + if extras: + for key in extras: + value = extras[key] + if isinstance(value, str): + cmd += ' --es' + elif isinstance(value, bool): + cmd += ' --ez' + elif isinstance(value, int): + cmd += ' --ei' + else: + raise NotImplementedError( + 'Need to teach StartActivity how to pass %s extras' % type(value)) + cmd += ' %s %s' % (key, value) + if trace_file_name: + cmd += ' --start-profiler ' + trace_file_name + if flags: + cmd += ' -f %s' % flags + return cmd + + def StartActivity(self, package, activity, wait_for_completion=False, + action='android.intent.action.VIEW', + category=None, data=None, + extras=None, trace_file_name=None, + force_stop=False, flags=None): + """Starts |package|'s activity on the device. + + Args: + package: Name of package to start (e.g. 'com.google.android.apps.chrome'). + activity: Name of activity (e.g. '.Main' or + 'com.google.android.apps.chrome.Main'). + wait_for_completion: wait for the activity to finish launching (-W flag). + action: string (e.g. "android.intent.action.MAIN"). Default is VIEW. + category: string (e.g. "android.intent.category.HOME") + data: Data string to pass to activity (e.g. 'http://www.example.com/'). + extras: Dict of extras to pass to activity. Values are significant. + trace_file_name: If used, turns on and saves the trace to this file name. + force_stop: force stop the target app before starting the activity (-S + flag). + Returns: + The output of the underlying command as a list of lines. + """ + cmd = self._GetActivityCommand(package, activity, wait_for_completion, + action, category, data, extras, + trace_file_name, force_stop, flags) + return self.RunShellCommand(cmd) + + def StartActivityTimed(self, package, activity, wait_for_completion=False, + action='android.intent.action.VIEW', + category=None, data=None, + extras=None, trace_file_name=None, + force_stop=False, flags=None): + """Starts |package|'s activity on the device, returning the start time + + Args - as for StartActivity + + Returns: + A tuple containing: + - the output of the underlying command as a list of lines, and + - a timestamp string for the time at which the activity started + """ + cmd = self._GetActivityCommand(package, activity, wait_for_completion, + action, category, data, extras, + trace_file_name, force_stop, flags) + self.StartMonitoringLogcat() + out = self.RunShellCommand('log starting activity; ' + cmd) + activity_started_re = re.compile('.*starting activity.*') + m = self.WaitForLogMatch(activity_started_re, None) + assert m + start_line = m.group(0) + return (out, GetLogTimestamp(start_line, self.GetDeviceYear())) + + def StartCrashUploadService(self, package): + # TODO(frankf): We really need a python wrapper around Intent + # to be shared with StartActivity/BroadcastIntent. + cmd = ( + 'am startservice -a %s.crash.ACTION_FIND_ALL -n ' + '%s/%s.crash.MinidumpUploadService' % + (constants.PACKAGE_INFO['chrome'].package, + package, + constants.PACKAGE_INFO['chrome'].package)) + am_output = self.RunShellCommandWithSU(cmd) + assert am_output and 'Starting' in am_output[-1], ( + 'Service failed to start: %s' % am_output) + time.sleep(15) + + def BroadcastIntent(self, package, intent, *args): + """Send a broadcast intent. + + Args: + package: Name of package containing the intent. + intent: Name of the intent. + args: Optional extra arguments for the intent. + """ + cmd = 'am broadcast -a %s.%s %s' % (package, intent, ' '.join(args)) + self.RunShellCommand(cmd) + + def GoHome(self): + """Tell the device to return to the home screen. Blocks until completion.""" + self.RunShellCommand('am start -W ' + '-a android.intent.action.MAIN -c android.intent.category.HOME') + + def CloseApplication(self, package): + """Attempt to close down the application, using increasing violence. + + Args: + package: Name of the process to kill off, e.g. + com.google.android.apps.chrome + """ + self.RunShellCommand('am force-stop ' + package) + + def GetApplicationPath(self, package): + """Get the installed apk path on the device for the given package. + + Args: + package: Name of the package. + + Returns: + Path to the apk on the device if it exists, None otherwise. + """ + pm_path_output = self.RunShellCommand('pm path ' + package) + # The path output contains anything if and only if the package + # exists. + if pm_path_output: + # pm_path_output is of the form: "package:/path/to/foo.apk" + return pm_path_output[0].split(':')[1] + else: + return None + + def ClearApplicationState(self, package): + """Closes and clears all state for the given |package|.""" + # Check that the package exists before clearing it. Necessary because + # calling pm clear on a package that doesn't exist may never return. + pm_path_output = self.RunShellCommand('pm path ' + package) + # The path output only contains anything if and only if the package exists. + if pm_path_output: + self.RunShellCommand('pm clear ' + package) + + def SendKeyEvent(self, keycode): + """Sends keycode to the device. + + Args: + keycode: Numeric keycode to send (see "enum" at top of file). + """ + self.RunShellCommand('input keyevent %d' % keycode) + + def _RunMd5Sum(self, host_path, device_path): + """Gets the md5sum of a host path and device path. + + Args: + host_path: Path (file or directory) on the host. + device_path: Path on the device. + + Returns: + A tuple containing lists of the host and device md5sum results as + created by _ParseMd5SumOutput(). + """ + md5sum_dist_path = os.path.join(constants.GetOutDirectory(), + 'md5sum_dist') + assert os.path.exists(md5sum_dist_path), 'Please build md5sum.' + md5sum_dist_mtime = os.stat(md5sum_dist_path).st_mtime + if (md5sum_dist_path not in self._push_if_needed_cache or + self._push_if_needed_cache[md5sum_dist_path] != md5sum_dist_mtime): + command = 'push %s %s' % (md5sum_dist_path, MD5SUM_DEVICE_FOLDER) + assert _HasAdbPushSucceeded(self._adb.SendCommand(command)) + self._push_if_needed_cache[md5sum_dist_path] = md5sum_dist_mtime + + (_, md5_device_output) = self.GetAndroidToolStatusAndOutput( + self._util_wrapper + ' ' + MD5SUM_DEVICE_PATH + ' ' + device_path, + lib_path=MD5SUM_DEVICE_FOLDER, + timeout_time=2 * 60) + device_hash_tuples = _ParseMd5SumOutput(md5_device_output) + assert os.path.exists(host_path), 'Local path not found %s' % host_path + md5sum_output = cmd_helper.GetCmdOutput( + [os.path.join(constants.GetOutDirectory(), 'md5sum_bin_host'), + host_path]) + host_hash_tuples = _ParseMd5SumOutput(md5sum_output.splitlines()) + return (host_hash_tuples, device_hash_tuples) + + def GetFilesChanged(self, host_path, device_path, ignore_filenames=False): + """Compares the md5sum of a host path against a device path. + + Note: Ignores extra files on the device. + + Args: + host_path: Path (file or directory) on the host. + device_path: Path on the device. + ignore_filenames: If True only the file contents are considered when + checking whether a file has changed, otherwise the relative path + must also match. + + Returns: + A list of tuples of the form (host_path, device_path) for files whose + md5sums do not match. + """ + + # Md5Sum resolves symbolic links in path names so the calculation of + # relative path names from its output will need the real path names of the + # base directories. Having calculated these they are used throughout the + # function since this makes us less subject to any future changes to Md5Sum. + real_host_path = os.path.realpath(host_path) + real_device_path = self.RunShellCommand('realpath "%s"' % device_path)[0] + + host_hash_tuples, device_hash_tuples = self._RunMd5Sum( + real_host_path, real_device_path) + + if len(host_hash_tuples) > len(device_hash_tuples): + logging.info('%s files do not exist on the device' % + (len(host_hash_tuples) - len(device_hash_tuples))) + + host_rel = [(os.path.relpath(os.path.normpath(t.path), real_host_path), + t.hash) + for t in host_hash_tuples] + + if os.path.isdir(real_host_path): + def RelToRealPaths(rel_path): + return (os.path.join(real_host_path, rel_path), + os.path.join(real_device_path, rel_path)) + else: + assert len(host_rel) == 1 + def RelToRealPaths(_): + return (real_host_path, real_device_path) + + if ignore_filenames: + # If we are ignoring file names, then we want to push any file for which + # a file with an equivalent MD5 sum does not exist on the device. + device_hashes = set([h.hash for h in device_hash_tuples]) + ShouldPush = lambda p, h: h not in device_hashes + else: + # Otherwise, we want to push any file on the host for which a file with + # an equivalent MD5 sum does not exist at the same relative path on the + # device. + device_rel = dict([(os.path.relpath(os.path.normpath(t.path), + real_device_path), + t.hash) + for t in device_hash_tuples]) + ShouldPush = lambda p, h: p not in device_rel or h != device_rel[p] + + return [RelToRealPaths(path) for path, host_hash in host_rel + if ShouldPush(path, host_hash)] + + def PushIfNeeded(self, host_path, device_path): + """Pushes |host_path| to |device_path|. + + Works for files and directories. This method skips copying any paths in + |test_data_paths| that already exist on the device with the same hash. + + All pushed files can be removed by calling RemovePushedFiles(). + """ + MAX_INDIVIDUAL_PUSHES = 50 + if not os.path.exists(host_path): + raise device_errors.CommandFailedError( + 'Local path not found %s' % host_path, device=str(self)) + + # See if the file on the host changed since the last push (if any) and + # return early if it didn't. Note that this shortcut assumes that the tests + # on the device don't modify the files. + if not os.path.isdir(host_path): + if host_path in self._push_if_needed_cache: + host_path_mtime = self._push_if_needed_cache[host_path] + if host_path_mtime == os.stat(host_path).st_mtime: + return + + size = host_utils.GetRecursiveDiskUsage(host_path) + self._pushed_files.append(device_path) + self._potential_push_size += size + + if os.path.isdir(host_path): + self.RunShellCommand('mkdir -p "%s"' % device_path) + + changed_files = self.GetFilesChanged(host_path, device_path) + logging.info('Found %d files that need to be pushed to %s', + len(changed_files), device_path) + if not changed_files: + return + + def Push(host, device): + # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout + # of 60 seconds which isn't sufficient for a lot of users of this method. + push_command = 'push %s %s' % (host, device) + self._LogShell(push_command) + + # Retry push with increasing backoff if the device is busy. + retry = 0 + while True: + output = self._adb.SendCommand(push_command, timeout_time=30 * 60) + if _HasAdbPushSucceeded(output): + if not os.path.isdir(host_path): + self._push_if_needed_cache[host] = os.stat(host).st_mtime + return + if retry < 3: + retry += 1 + wait_time = 5 * retry + logging.error('Push failed, retrying in %d seconds: %s' % + (wait_time, output)) + time.sleep(wait_time) + else: + raise Exception('Push failed: %s' % output) + + diff_size = 0 + if len(changed_files) <= MAX_INDIVIDUAL_PUSHES: + diff_size = sum(host_utils.GetRecursiveDiskUsage(f[0]) + for f in changed_files) + + # TODO(craigdh): Replace this educated guess with a heuristic that + # approximates the push time for each method. + if len(changed_files) > MAX_INDIVIDUAL_PUSHES or diff_size > 0.5 * size: + self._actual_push_size += size + Push(host_path, device_path) + else: + for f in changed_files: + Push(f[0], f[1]) + self._actual_push_size += diff_size + + def GetPushSizeInfo(self): + """Get total size of pushes to the device done via PushIfNeeded() + + Returns: + A tuple: + 1. Total size of push requests to PushIfNeeded (MB) + 2. Total size that was actually pushed (MB) + """ + return (self._potential_push_size, self._actual_push_size) + + def GetFileContents(self, filename, log_result=False): + """Gets contents from the file specified by |filename|.""" + return self.RunShellCommand('cat "%s" 2>/dev/null' % filename, + log_result=log_result) + + def SetFileContents(self, filename, contents): + """Writes |contents| to the file specified by |filename|.""" + with tempfile.NamedTemporaryFile() as f: + f.write(contents) + f.flush() + self._adb.Push(f.name, filename) + + def RunShellCommandWithSU(self, command, timeout_time=20, log_result=False): + return self.RunShellCommand('su -c %s' % command, timeout_time, log_result) + + def CanAccessProtectedFileContents(self): + """Returns True if Get/SetProtectedFileContents would work via "su" or adb + shell running as root. + + Devices running user builds don't have adb root, but may provide "su" which + can be used for accessing protected files. + """ + return (self._GetProtectedFileCommandRunner() != None) + + def _GetProtectedFileCommandRunner(self): + """Finds the best method to access protected files on the device. + + Returns: + 1. None when privileged files cannot be accessed on the device. + 2. Otherwise: A function taking a single parameter: a string with command + line arguments. Running that function executes the command with + the appropriate method. + """ + if self._protected_file_access_method_initialized: + return self._privileged_command_runner + + self._privileged_command_runner = None + self._protected_file_access_method_initialized = True + + for cmd in [self.RunShellCommand, self.RunShellCommandWithSU]: + # Get contents of the auxv vector for the init(8) process from a small + # binary file that always exists on linux and is always read-protected. + contents = cmd('cat /proc/1/auxv') + # The leading 4 or 8-bytes of auxv vector is a_type. There are not many + # reserved a_type values, hence byte 2 must always be '\0' for a realistic + # auxv. See /usr/include/elf.h. + if len(contents) > 0 and (contents[0][2] == '\0'): + self._privileged_command_runner = cmd + break + return self._privileged_command_runner + + def GetProtectedFileContents(self, filename): + """Gets contents from the protected file specified by |filename|. + + This is potentially less efficient than GetFileContents. + """ + command = 'cat "%s" 2> /dev/null' % filename + command_runner = self._GetProtectedFileCommandRunner() + if command_runner: + return command_runner(command) + else: + logging.warning('Could not access protected file: %s' % filename) + return [] + + def SetProtectedFileContents(self, filename, contents): + """Writes |contents| to the protected file specified by |filename|. + + This is less efficient than SetFileContents. + """ + with DeviceTempFile(self) as temp_file: + with DeviceTempFile(self, suffix=".sh") as temp_script: + # Put the contents in a temporary file + self.SetFileContents(temp_file.name, contents) + # Create a script to copy the file contents to its final destination + self.SetFileContents(temp_script.name, + 'cat %s > %s' % (temp_file.name, filename)) + + command = 'sh %s' % temp_script.name + command_runner = self._GetProtectedFileCommandRunner() + if command_runner: + return command_runner(command) + else: + logging.warning( + 'Could not set contents of protected file: %s' % filename) + + + def RemovePushedFiles(self): + """Removes all files pushed with PushIfNeeded() from the device.""" + for p in self._pushed_files: + self.RunShellCommand('rm -r %s' % p, timeout_time=2 * 60) + + def ListPathContents(self, path): + """Lists files in all subdirectories of |path|. + + Args: + path: The path to list. + + Returns: + A dict of {"name": (size, lastmod), ...}. + """ + # Example output: + # /foo/bar: + # -rw-r----- user group 102 2011-05-12 12:29:54.131623387 +0100 baz.txt + re_file = re.compile('^-(?P[^\s]+)\s+' + '(?P[^\s]+)\s+' + '(?P[^\s]+)\s+' + '(?P[^\s]+)\s+' + '(?P[^\s]+)\s+' + '(?P__.zip. The file will +be extracted in the android_tools/sdk/extras directory on the test bots. This +script will not do anything for developers. + +TODO(navabi): Move this script (crbug.com/459819). +""" + +import json +import os +import shutil +import subprocess +import sys +import zipfile + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +CHROME_SRC = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir)) +sys.path.insert(0, os.path.join(SCRIPT_DIR, 'android')) +sys.path.insert(1, os.path.join(CHROME_SRC, 'tools')) + +from pylib import constants +import find_depot_tools + +DEPOT_PATH = find_depot_tools.add_depot_tools_to_path() +GSUTIL_PATH = os.path.join(DEPOT_PATH, 'gsutil.py') +SDK_EXTRAS_BUCKET = 'gs://chrome-sdk-extras' +SDK_EXTRAS_PATH = os.path.join(constants.ANDROID_SDK_ROOT, 'extras') +SDK_EXTRAS_JSON_FILE = os.path.join(os.path.dirname(__file__), + 'android_sdk_extras.json') + + +def clean_and_extract(dir_name, package_name, zip_file): + local_dir = '%s/%s/%s' % (SDK_EXTRAS_PATH, dir_name, package_name) + if os.path.exists(local_dir): + shutil.rmtree(local_dir) + local_zip = '%s/%s' % (SDK_EXTRAS_PATH, zip_file) + with zipfile.ZipFile(local_zip) as z: + z.extractall(path=SDK_EXTRAS_PATH) + + +def main(): + if not os.environ.get('CHROME_HEADLESS'): + # This is not a buildbot checkout. + return 0 + # Update the android_sdk_extras.json file to update downloaded packages. + with open(SDK_EXTRAS_JSON_FILE) as json_file: + packages = json.load(json_file) + for package in packages: + local_zip = '%s/%s' % (SDK_EXTRAS_PATH, package['zip']) + if not os.path.exists(local_zip): + package_zip = '%s/%s' % (SDK_EXTRAS_BUCKET, package['zip']) + try: + subprocess.check_call(['python', GSUTIL_PATH, '--force-version', '4.7', + 'cp', package_zip, local_zip]) + except subprocess.CalledProcessError: + print ('WARNING: Failed to download SDK packages. If this bot compiles ' + 'for Android, it may have errors.') + return 0 + # Always clean dir and extract zip to ensure correct contents. + clean_and_extract(package['dir_name'], package['package'], package['zip']) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/env_dump.py b/engine/src/flutter/build/env_dump.py new file mode 100755 index 0000000000..21edfe633c --- /dev/null +++ b/engine/src/flutter/build/env_dump.py @@ -0,0 +1,56 @@ +#!/usr/bin/python +# Copyright 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. + +# This script can either source a file and dump the enironment changes done by +# it, or just simply dump the current environment as JSON into a file. + +import json +import optparse +import os +import pipes +import subprocess +import sys + + +def main(): + parser = optparse.OptionParser() + parser.add_option('-f', '--output-json', + help='File to dump the environment as JSON into.') + parser.add_option( + '-d', '--dump-mode', action='store_true', + help='Dump the environment to sys.stdout and exit immediately.') + + parser.disable_interspersed_args() + options, args = parser.parse_args() + if options.dump_mode: + if args or options.output_json: + parser.error('Cannot specify args or --output-json with --dump-mode.') + json.dump(dict(os.environ), sys.stdout) + else: + if not options.output_json: + parser.error('Requires --output-json option.') + + envsetup_cmd = ' '.join(map(pipes.quote, args)) + full_cmd = [ + 'bash', '-c', + '. %s > /dev/null; %s -d' % (envsetup_cmd, os.path.abspath(__file__)) + ] + try: + output = subprocess.check_output(full_cmd) + except Exception as e: + sys.exit('Error running %s and dumping environment.' % envsetup_cmd) + + env_diff = {} + new_env = json.loads(output) + for k, val in new_env.items(): + if k == '_' or (k in os.environ and os.environ[k] == val): + continue + env_diff[k] = val + with open(options.output_json, 'w') as f: + json.dump(env_diff, f) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/extract_from_cab.py b/engine/src/flutter/build/extract_from_cab.py new file mode 100755 index 0000000000..080370ca9a --- /dev/null +++ b/engine/src/flutter/build/extract_from_cab.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Extracts a single file from a CAB archive.""" + +import os +import shutil +import subprocess +import sys +import tempfile + +def run_quiet(*args): + """Run 'expand' suppressing noisy output. Returns returncode from process.""" + popen = subprocess.Popen(args, stdout=subprocess.PIPE) + out, _ = popen.communicate() + if popen.returncode: + # expand emits errors to stdout, so if we fail, then print that out. + print out + return popen.returncode + +def main(): + if len(sys.argv) != 4: + print 'Usage: extract_from_cab.py cab_path archived_file output_dir' + return 1 + + [cab_path, archived_file, output_dir] = sys.argv[1:] + + # Expand.exe does its work in a fixed-named temporary directory created within + # the given output directory. This is a problem for concurrent extractions, so + # create a unique temp dir within the desired output directory to work around + # this limitation. + temp_dir = tempfile.mkdtemp(dir=output_dir) + + try: + # Invoke the Windows expand utility to extract the file. + level = run_quiet('expand', cab_path, '-F:' + archived_file, temp_dir) + if level == 0: + # Move the output file into place, preserving expand.exe's behavior of + # paving over any preexisting file. + output_file = os.path.join(output_dir, archived_file) + try: + os.remove(output_file) + except OSError: + pass + os.rename(os.path.join(temp_dir, archived_file), output_file) + finally: + shutil.rmtree(temp_dir, True) + + if level != 0: + return level + + # The expand utility preserves the modification date and time of the archived + # file. Touch the extracted file. This helps build systems that compare the + # modification times of input and output files to determine whether to do an + # action. + os.utime(os.path.join(output_dir, archived_file), None) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/find_isolated_tests.py b/engine/src/flutter/build/find_isolated_tests.py new file mode 100755 index 0000000000..c5b3ab77a9 --- /dev/null +++ b/engine/src/flutter/build/find_isolated_tests.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +"""Scans build output directory for .isolated files, calculates their SHA1 +hashes, stores final list in JSON document and then removes *.isolated files +found (to ensure no stale *.isolated stay around on the next build). + +Used to figure out what tests were build in isolated mode to trigger these +tests to run on swarming. + +For more info see: +https://sites.google.com/a/chromium.org/dev/developers/testing/isolated-testing +""" + +import glob +import hashlib +import json +import optparse +import os +import re +import sys + + +def hash_file(filepath): + """Calculates the hash of a file without reading it all in memory at once.""" + digest = hashlib.sha1() + with open(filepath, 'rb') as f: + while True: + chunk = f.read(1024*1024) + if not chunk: + break + digest.update(chunk) + return digest.hexdigest() + + +def main(): + parser = optparse.OptionParser( + usage='%prog --build-dir --output-json ', + description=sys.modules[__name__].__doc__) + parser.add_option( + '--build-dir', + help='Path to a directory to search for *.isolated files.') + parser.add_option( + '--output-json', + help='File to dump JSON results into.') + + options, _ = parser.parse_args() + if not options.build_dir: + parser.error('--build-dir option is required') + if not options.output_json: + parser.error('--output-json option is required') + + result = {} + + # Get the file hash values and output the pair. + pattern = os.path.join(options.build_dir, '*.isolated') + for filepath in sorted(glob.glob(pattern)): + test_name = os.path.splitext(os.path.basename(filepath))[0] + if re.match(r'^.+?\.\d$', test_name): + # It's a split .isolated file, e.g. foo.0.isolated. Ignore these. + continue + + # TODO(csharp): Remove deletion once the isolate tracked dependencies are + # inputs for the isolated files. + sha1_hash = hash_file(filepath) + os.remove(filepath) + result[test_name] = sha1_hash + + with open(options.output_json, 'wb') as f: + json.dump(result, f) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/gdb-add-index b/engine/src/flutter/build/gdb-add-index new file mode 100755 index 0000000000..992ac16159 --- /dev/null +++ b/engine/src/flutter/build/gdb-add-index @@ -0,0 +1,162 @@ +#!/bin/bash +# Copyright (c) 2012 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. +# +# Saves the gdb index for a given binary and its shared library dependencies. +# +# This will run gdb index in parallel on a number of binaries using SIGUSR1 +# as the communication mechanism to simulate a semaphore. Because of the +# nature of this technique, using "set -e" is very difficult. The SIGUSR1 +# terminates a "wait" with an error which we need to interpret. +# +# When modifying this code, most of the real logic is in the index_one_file +# function. The rest is cleanup + sempahore plumbing. + +# Cleanup temp directory and ensure all child jobs are dead-dead. +function on_exit { + trap "" EXIT USR1 # Avoid reentrancy. + + local jobs=$(jobs -p) + if [ -n "$jobs" ]; then + echo -n "Killing outstanding index jobs..." + kill -KILL $(jobs -p) + wait + echo "done" + fi + + if [ -f "$DIRECTORY" ]; then + echo -n "Removing temp directory $DIRECTORY..." + rm -rf $DIRECTORY + echo done + fi +} + +# Add index to one binary. +function index_one_file { + local file=$1 + local basename=$(basename "$file") + local should_index="${SHOULD_INDEX}" + + local readelf_out=$(${TOOLCHAIN_PREFIX}readelf -S "$file") + if [[ $readelf_out =~ "gdb_index" ]]; then + if [ "${REMOVE_INDEX}" = 1 ]; then + ${TOOLCHAIN_PREFIX}objcopy --remove-section .gdb_index "$file" + echo "Removed index from $basename." + else + echo "Skipped $basename -- already contains index." + should_index=0 + fi + fi + + if [ "${should_index}" = 1 ]; then + local start=$(date +"%s%N") + echo "Adding index to $basename..." + + ${TOOLCHAIN_PREFIX}gdb -batch "$file" -ex "save gdb-index $DIRECTORY" \ + -ex "quit" + local index_file="$DIRECTORY/$basename.gdb-index" + if [ -f "$index_file" ]; then + ${TOOLCHAIN_PREFIX}objcopy --add-section .gdb_index="$index_file" \ + --set-section-flags .gdb_index=readonly "$file" "$file" + local finish=$(date +"%s%N") + local elapsed=$(((finish - start)/1000000)) + echo " ...$basename indexed. [${elapsed}ms]" + else + echo " ...$basename unindexable." + fi + fi +} + +# Functions that when combined, concurrently index all files in FILES_TO_INDEX +# array. The global FILES_TO_INDEX is declared in the main body of the script. +function async_index { + # Start a background subshell to run the index command. + { + index_one_file $1 + kill -SIGUSR1 $$ # $$ resolves to the parent script. + exit 129 # See comment above wait loop at bottom. + } & +} + +CUR_FILE_NUM=0 +function index_next { + if (( CUR_FILE_NUM >= ${#FILES_TO_INDEX[@]} )); then + return + fi + + async_index "${FILES_TO_INDEX[CUR_FILE_NUM]}" + ((CUR_FILE_NUM += 1)) || true +} + + +######## +### Main body of the script. + +REMOVE_INDEX=0 +SHOULD_INDEX=1 +while getopts ":f:r" opt; do + case $opt in + f) + REMOVE_INDEX=1 + shift + ;; + r) + REMOVE_INDEX=1 + SHOULD_INDEX=0 + shift + ;; + *) + echo "Invalid option: -$OPTARG" >&2 + ;; + esac +done + +if [[ ! $# == 1 ]]; then + echo "Usage: $0 [-f] [-r] path-to-binary" + echo " -f forces replacement of an existing index." + echo " -r removes the index section." + exit 1 +fi + +FILENAME="$1" +if [[ ! -f "$FILENAME" ]]; then + echo "Path $FILENAME does not exist." + exit 1 +fi + +# Ensure we cleanup on on exit. +trap on_exit EXIT + +# We're good to go! Create temp directory for index files. +DIRECTORY=$(mktemp -d) +echo "Made temp directory $DIRECTORY." + +# Create array with the filename and all shared libraries that +# have the same dirname. The dirname is a signal that these +# shared libraries were part of the same build as the binary. +declare -a FILES_TO_INDEX=($FILENAME + $(ldd "$FILENAME" 2>/dev/null \ + | grep $(dirname "$FILENAME") \ + | sed "s/.*[ \t]\(.*\) (.*/\1/") +) + +# Start concurrent indexing. +trap index_next USR1 + +# 4 is an arbitrary default. When changing, remember we are likely IO bound +# so basing this off the number of cores is not sensible. +INDEX_TASKS=${INDEX_TASKS:-4} +for ((i=0;i<${INDEX_TASKS};i++)); do + index_next +done + +# Do a wait loop. Bash waits that terminate due a trap have an exit +# code > 128. We also ensure that our subshell's "normal" exit occurs with +# an exit code > 128. This allows us to do consider a > 128 exit code as +# an indication that the loop should continue. Unfortunately, it also means +# we cannot use set -e since technically the "wait" is failing. +wait +while (( $? > 128 )); do + wait +done diff --git a/engine/src/flutter/build/get_landmines.py b/engine/src/flutter/build/get_landmines.py new file mode 100755 index 0000000000..26b2565858 --- /dev/null +++ b/engine/src/flutter/build/get_landmines.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# Copyright 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. + +""" +This file emits the list of reasons why a particular build needs to be clobbered +(or a list of 'landmines'). +""" + +import sys + +import landmine_utils + + +builder = landmine_utils.builder +distributor = landmine_utils.distributor +gyp_defines = landmine_utils.gyp_defines +gyp_msvs_version = landmine_utils.gyp_msvs_version +platform = landmine_utils.platform + + +def print_landmines(): + """ + ALL LANDMINES ARE EMITTED FROM HERE. + """ + # DO NOT add landmines as part of a regular CL. Landmines are a last-effort + # bandaid fix if a CL that got landed has a build dependency bug and all bots + # need to be cleaned up. If you're writing a new CL that causes build + # dependency problems, fix the dependency problems instead of adding a + # landmine. + + if (distributor() == 'goma' and platform() == 'win32' and + builder() == 'ninja'): + print 'Need to clobber winja goma due to backend cwd cache fix.' + if platform() == 'android': + print 'Clobber because of unions.' + if platform() == 'win' and builder() == 'ninja': + print 'Compile on cc_unittests fails due to symbols removed in r185063.' + if platform() == 'linux' and builder() == 'ninja': + print 'Builders switching from make to ninja will clobber on this.' + if platform() == 'mac': + print 'Switching from bundle to unbundled dylib (issue 14743002).' + if platform() in ('win', 'mac'): + print ('Improper dependency for create_nmf.py broke in r240802, ' + 'fixed in r240860.') + if (platform() == 'win' and builder() == 'ninja' and + gyp_msvs_version() == '2012' and + gyp_defines().get('target_arch') == 'x64' and + gyp_defines().get('dcheck_always_on') == '1'): + print "Switched win x64 trybots from VS2010 to VS2012." + if (platform() == 'win' and builder() == 'ninja' and + gyp_msvs_version().startswith('2013')): + print "Switched win from VS2010 to VS2013." + print "Update to VS2013 Update 2." + print "Update to VS2013 Update 4." + if (platform() == 'win' and gyp_msvs_version().startswith('2015')): + print 'Switch to VS2015' + print 'Need to clobber everything due to an IDL change in r154579 (blink)' + print 'Need to clobber everything due to gen file moves in r175513 (Blink)' + if (platform() != 'ios'): + print 'Clobber to get rid of obselete test plugin after r248358' + print 'Clobber to rebuild GN files for V8' + print 'Clobber to get rid of stale generated mojom.h files' + print 'Need to clobber everything due to build_nexe change in nacl r13424' + print '[chromium-dev] PSA: clobber build needed for IDR_INSPECTOR_* compil...' + print 'blink_resources.grd changed: crbug.com/400860' + print 'ninja dependency cycle: crbug.com/408192' + print 'Clobber to fix missing NaCl gyp dependencies (crbug.com/427427).' + print 'Another clobber for missing NaCl gyp deps (crbug.com/427427).' + print 'Clobber to fix GN not picking up increased ID range (crbug.com/444902)' + print 'Remove NaCl toolchains from the output dir (crbug.com/456902)' + if platform() == 'ios': + print 'Clobber iOS to workaround Xcode deps bug (crbug.com/485435)' + print 'Clobber: https://github.com/domokit/mojo/issues/269' + + +def main(): + print_landmines() + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/get_sdk_extras_packages.py b/engine/src/flutter/build/get_sdk_extras_packages.py new file mode 100755 index 0000000000..6d870cc86f --- /dev/null +++ b/engine/src/flutter/build/get_sdk_extras_packages.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# Copyright (c) 2014 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. + +import json +import os +import sys + +SDK_EXTRAS_JSON_FILE = os.path.join(os.path.dirname(__file__), + 'android_sdk_extras.json') + +def main(): + with open(SDK_EXTRAS_JSON_FILE) as json_file: + packages = json.load(json_file) + for package in packages: + print package['package'].replace('_', ' ') + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/get_syzygy_binaries.py b/engine/src/flutter/build/get_syzygy_binaries.py new file mode 100755 index 0000000000..2577c7cb7f --- /dev/null +++ b/engine/src/flutter/build/get_syzygy_binaries.py @@ -0,0 +1,451 @@ +#!/usr/bin/env python +# Copyright 2014 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 utility script for downloading versioned Syzygy binaries.""" + +import cStringIO +import hashlib +import errno +import json +import logging +import optparse +import os +import re +import shutil +import stat +import sys +import subprocess +import urllib2 +import zipfile + + +_LOGGER = logging.getLogger(os.path.basename(__file__)) + +# The URL where official builds are archived. +_SYZYGY_ARCHIVE_URL = ('https://syzygy-archive.commondatastorage.googleapis.com' + '/builds/official/%(revision)s') + +# A JSON file containing the state of the download directory. If this file and +# directory state do not agree, then the binaries will be downloaded and +# installed again. +_STATE = '.state' + +# This matches an integer (an SVN revision number) or a SHA1 value (a GIT hash). +# The archive exclusively uses lowercase GIT hashes. +_REVISION_RE = re.compile('^(?:\d+|[a-f0-9]{40})$') + +# This matches an MD5 hash. +_MD5_RE = re.compile('^[a-f0-9]{32}$') + +# List of reources to be downloaded and installed. These are tuples with the +# following format: +# (basename, logging name, relative installation path, extraction filter) +_RESOURCES = [ + ('benchmark.zip', 'benchmark', '', None), + ('binaries.zip', 'binaries', 'exe', None), + ('symbols.zip', 'symbols', 'exe', + lambda x: x.filename.endswith('.dll.pdb'))] + + +def _Shell(*cmd, **kw): + """Runs |cmd|, returns the results from Popen(cmd).communicate().""" + _LOGGER.debug('Executing %s.', cmd) + prog = subprocess.Popen(cmd, shell=True, **kw) + + stdout, stderr = prog.communicate() + if prog.returncode != 0: + raise RuntimeError('Command "%s" returned %d.' % (cmd, prog.returncode)) + return (stdout, stderr) + + +def _LoadState(output_dir): + """Loads the contents of the state file for a given |output_dir|, returning + None if it doesn't exist. + """ + path = os.path.join(output_dir, _STATE) + if not os.path.exists(path): + _LOGGER.debug('No state file found.') + return None + with open(path, 'rb') as f: + _LOGGER.debug('Reading state file: %s', path) + try: + return json.load(f) + except ValueError: + _LOGGER.debug('Invalid state file.') + return None + + +def _SaveState(output_dir, state, dry_run=False): + """Saves the |state| dictionary to the given |output_dir| as a JSON file.""" + path = os.path.join(output_dir, _STATE) + _LOGGER.debug('Writing state file: %s', path) + if dry_run: + return + with open(path, 'wb') as f: + f.write(json.dumps(state, sort_keys=True, indent=2)) + + +def _Md5(path): + """Returns the MD5 hash of the file at |path|, which must exist.""" + return hashlib.md5(open(path, 'rb').read()).hexdigest() + + +def _StateIsValid(state): + """Returns true if the given state structure is valid.""" + if not isinstance(state, dict): + _LOGGER.debug('State must be a dict.') + return False + r = state.get('revision', None) + if not isinstance(r, basestring) or not _REVISION_RE.match(r): + _LOGGER.debug('State contains an invalid revision.') + return False + c = state.get('contents', None) + if not isinstance(c, dict): + _LOGGER.debug('State must contain a contents dict.') + return False + for (relpath, md5) in c.iteritems(): + if not isinstance(relpath, basestring) or len(relpath) == 0: + _LOGGER.debug('State contents dict contains an invalid path.') + return False + if not isinstance(md5, basestring) or not _MD5_RE.match(md5): + _LOGGER.debug('State contents dict contains an invalid MD5 digest.') + return False + return True + + +def _BuildActualState(stored, revision, output_dir): + """Builds the actual state using the provided |stored| state as a template. + Only examines files listed in the stored state, causing the script to ignore + files that have been added to the directories locally. |stored| must be a + valid state dictionary. + """ + contents = {} + state = { 'revision': revision, 'contents': contents } + for relpath, md5 in stored['contents'].iteritems(): + abspath = os.path.abspath(os.path.join(output_dir, relpath)) + if os.path.isfile(abspath): + m = _Md5(abspath) + contents[relpath] = m + + return state + + +def _StatesAreConsistent(stored, actual): + """Validates whether two state dictionaries are consistent. Both must be valid + state dictionaries. Additional entries in |actual| are ignored. + """ + if stored['revision'] != actual['revision']: + _LOGGER.debug('Mismatched revision number.') + return False + cont_stored = stored['contents'] + cont_actual = actual['contents'] + for relpath, md5 in cont_stored.iteritems(): + if relpath not in cont_actual: + _LOGGER.debug('Missing content: %s', relpath) + return False + if md5 != cont_actual[relpath]: + _LOGGER.debug('Modified content: %s', relpath) + return False + return True + + +def _GetCurrentState(revision, output_dir): + """Loads the current state and checks to see if it is consistent. Returns + a tuple (state, bool). The returned state will always be valid, even if an + invalid state is present on disk. + """ + stored = _LoadState(output_dir) + if not _StateIsValid(stored): + _LOGGER.debug('State is invalid.') + # Return a valid but empty state. + return ({'revision': '0', 'contents': {}}, False) + actual = _BuildActualState(stored, revision, output_dir) + # If the script has been modified consider the state invalid. + path = os.path.join(output_dir, _STATE) + if os.path.getmtime(__file__) > os.path.getmtime(path): + return (stored, False) + # Otherwise, explicitly validate the state. + if not _StatesAreConsistent(stored, actual): + return (stored, False) + return (stored, True) + + +def _DirIsEmpty(path): + """Returns true if the given directory is empty, false otherwise.""" + for root, dirs, files in os.walk(path): + return not dirs and not files + + +def _RmTreeHandleReadOnly(func, path, exc): + """An error handling function for use with shutil.rmtree. This will + detect failures to remove read-only files, and will change their properties + prior to removing them. This is necessary on Windows as os.remove will return + an access error for read-only files, and git repos contain read-only + pack/index files. + """ + excvalue = exc[1] + if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES: + _LOGGER.debug('Removing read-only path: %s', path) + os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + func(path) + else: + raise + + +def _RmTree(path): + """A wrapper of shutil.rmtree that handles read-only files.""" + shutil.rmtree(path, ignore_errors=False, onerror=_RmTreeHandleReadOnly) + + +def _CleanState(output_dir, state, dry_run=False): + """Cleans up files/directories in |output_dir| that are referenced by + the given |state|. Raises an error if there are local changes. Returns a + dictionary of files that were deleted. + """ + _LOGGER.debug('Deleting files from previous installation.') + deleted = {} + + # Generate a list of files to delete, relative to |output_dir|. + contents = state['contents'] + files = sorted(contents.keys()) + + # Try to delete the files. Keep track of directories to delete as well. + dirs = {} + for relpath in files: + fullpath = os.path.join(output_dir, relpath) + fulldir = os.path.dirname(fullpath) + dirs[fulldir] = True + if os.path.exists(fullpath): + # If somehow the file has become a directory complain about it. + if os.path.isdir(fullpath): + raise Exception('Directory exists where file expected: %s' % fullpath) + + # Double check that the file doesn't have local changes. If it does + # then refuse to delete it. + if relpath in contents: + stored_md5 = contents[relpath] + actual_md5 = _Md5(fullpath) + if actual_md5 != stored_md5: + raise Exception('File has local changes: %s' % fullpath) + + # The file is unchanged so it can safely be deleted. + _LOGGER.debug('Deleting file "%s".', fullpath) + deleted[relpath] = True + if not dry_run: + os.unlink(fullpath) + + # Sort directories from longest name to shortest. This lets us remove empty + # directories from the most nested paths first. + dirs = sorted(dirs.keys(), key=lambda x: len(x), reverse=True) + for p in dirs: + if os.path.exists(p) and _DirIsEmpty(p): + _LOGGER.debug('Deleting empty directory "%s".', p) + if not dry_run: + _RmTree(p) + + return deleted + + +def _Download(url): + """Downloads the given URL and returns the contents as a string.""" + response = urllib2.urlopen(url) + if response.code != 200: + raise RuntimeError('Failed to download "%s".' % url) + return response.read() + + +def _InstallBinaries(options, deleted={}): + """Installs Syzygy binaries. This assumes that the output directory has + already been cleaned, as it will refuse to overwrite existing files.""" + contents = {} + state = { 'revision': options.revision, 'contents': contents } + archive_url = _SYZYGY_ARCHIVE_URL % { 'revision': options.revision } + if options.resources: + resources = [(resource, resource, '', None) + for resource in options.resources] + else: + resources = _RESOURCES + for (base, name, subdir, filt) in resources: + # Create the output directory if it doesn't exist. + fulldir = os.path.join(options.output_dir, subdir) + if os.path.isfile(fulldir): + raise Exception('File exists where a directory needs to be created: %s' % + fulldir) + if not os.path.exists(fulldir): + _LOGGER.debug('Creating directory: %s', fulldir) + if not options.dry_run: + os.makedirs(fulldir) + + # Download the archive. + url = archive_url + '/' + base + _LOGGER.debug('Retrieving %s archive at "%s".', name, url) + data = _Download(url) + + _LOGGER.debug('Unzipping %s archive.', name) + archive = zipfile.ZipFile(cStringIO.StringIO(data)) + for entry in archive.infolist(): + if not filt or filt(entry): + fullpath = os.path.normpath(os.path.join(fulldir, entry.filename)) + relpath = os.path.relpath(fullpath, options.output_dir) + if os.path.exists(fullpath): + # If in a dry-run take into account the fact that the file *would* + # have been deleted. + if options.dry_run and relpath in deleted: + pass + else: + raise Exception('Path already exists: %s' % fullpath) + + # Extract the file and update the state dictionary. + _LOGGER.debug('Extracting "%s".', fullpath) + if not options.dry_run: + archive.extract(entry.filename, fulldir) + md5 = _Md5(fullpath) + contents[relpath] = md5 + if sys.platform == 'cygwin': + os.chmod(fullpath, os.stat(fullpath).st_mode | stat.S_IXUSR) + + return state + + +def _ParseCommandLine(): + """Parses the command-line and returns an options structure.""" + option_parser = optparse.OptionParser() + option_parser.add_option('--dry-run', action='store_true', default=False, + help='If true then will simply list actions that would be performed.') + option_parser.add_option('--force', action='store_true', default=False, + help='Force an installation even if the binaries are up to date.') + option_parser.add_option('--output-dir', type='string', + help='The path where the binaries will be replaced. Existing binaries ' + 'will only be overwritten if not up to date.') + option_parser.add_option('--overwrite', action='store_true', default=False, + help='If specified then the installation will happily delete and rewrite ' + 'the entire output directory, blasting any local changes.') + option_parser.add_option('--revision', type='string', + help='The SVN revision or GIT hash associated with the required version.') + option_parser.add_option('--revision-file', type='string', + help='A text file containing an SVN revision or GIT hash.') + option_parser.add_option('--resource', type='string', action='append', + dest='resources', help='A resource to be downloaded.') + option_parser.add_option('--verbose', dest='log_level', action='store_const', + default=logging.INFO, const=logging.DEBUG, + help='Enables verbose logging.') + option_parser.add_option('--quiet', dest='log_level', action='store_const', + default=logging.INFO, const=logging.ERROR, + help='Disables all output except for errors.') + options, args = option_parser.parse_args() + if args: + option_parser.error('Unexpected arguments: %s' % args) + if not options.output_dir: + option_parser.error('Must specify --output-dir.') + if not options.revision and not options.revision_file: + option_parser.error('Must specify one of --revision or --revision-file.') + if options.revision and options.revision_file: + option_parser.error('Must not specify both --revision and --revision-file.') + + # Configure logging. + logging.basicConfig(level=options.log_level) + + # If a revision file has been specified then read it. + if options.revision_file: + options.revision = open(options.revision_file, 'rb').read().strip() + _LOGGER.debug('Parsed revision "%s" from file "%s".', + options.revision, options.revision_file) + + # Ensure that the specified SVN revision or GIT hash is valid. + if not _REVISION_RE.match(options.revision): + option_parser.error('Must specify a valid SVN or GIT revision.') + + # This just makes output prettier to read. + options.output_dir = os.path.normpath(options.output_dir) + + return options + + +def _RemoveOrphanedFiles(options): + """This is run on non-Windows systems to remove orphaned files that may have + been downloaded by a previous version of this script. + """ + # Reconfigure logging to output info messages. This will allow inspection of + # cleanup status on non-Windows buildbots. + _LOGGER.setLevel(logging.INFO) + + output_dir = os.path.abspath(options.output_dir) + + # We only want to clean up the folder in 'src/third_party/syzygy', and we + # expect to be called with that as an output directory. This is an attempt to + # not start deleting random things if the script is run from an alternate + # location, or not called from the gclient hooks. + expected_syzygy_dir = os.path.abspath(os.path.join( + os.path.dirname(__file__), '..', 'third_party', 'syzygy')) + expected_output_dir = os.path.join(expected_syzygy_dir, 'binaries') + if expected_output_dir != output_dir: + _LOGGER.info('Unexpected output directory, skipping cleanup.') + return + + if not os.path.isdir(expected_syzygy_dir): + _LOGGER.info('Output directory does not exist, skipping cleanup.') + return + + def OnError(function, path, excinfo): + """Logs error encountered by shutil.rmtree.""" + _LOGGER.error('Error when running %s(%s)', function, path, exc_info=excinfo) + + _LOGGER.info('Removing orphaned files from %s', expected_syzygy_dir) + if not options.dry_run: + shutil.rmtree(expected_syzygy_dir, True, OnError) + + +def main(): + options = _ParseCommandLine() + + if options.dry_run: + _LOGGER.debug('Performing a dry-run.') + + # We only care about Windows platforms, as the Syzygy binaries aren't used + # elsewhere. However, there was a short period of time where this script + # wasn't gated on OS types, and those OSes downloaded and installed binaries. + # This will cleanup orphaned files on those operating systems. + if sys.platform not in ('win32', 'cygwin'): + return _RemoveOrphanedFiles(options) + + # Load the current installation state, and validate it against the + # requested installation. + state, is_consistent = _GetCurrentState(options.revision, options.output_dir) + + # Decide whether or not an install is necessary. + if options.force: + _LOGGER.debug('Forcing reinstall of binaries.') + elif is_consistent: + # Avoid doing any work if the contents of the directory are consistent. + _LOGGER.debug('State unchanged, no reinstall necessary.') + return + + # Under normal logging this is the only only message that will be reported. + _LOGGER.info('Installing revision %s Syzygy binaries.', + options.revision[0:12]) + + # Clean up the old state to begin with. + deleted = [] + if options.overwrite: + if os.path.exists(options.output_dir): + # If overwrite was specified then take a heavy-handed approach. + _LOGGER.debug('Deleting entire installation directory.') + if not options.dry_run: + _RmTree(options.output_dir) + else: + # Otherwise only delete things that the previous installation put in place, + # and take care to preserve any local changes. + deleted = _CleanState(options.output_dir, state, options.dry_run) + + # Install the new binaries. In a dry-run this will actually download the + # archives, but it won't write anything to disk. + state = _InstallBinaries(options, deleted) + + # Build and save the state for the directory. + _SaveState(options.output_dir, state, options.dry_run) + + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/build/git-hooks/OWNERS b/engine/src/flutter/build/git-hooks/OWNERS new file mode 100644 index 0000000000..3e327dc711 --- /dev/null +++ b/engine/src/flutter/build/git-hooks/OWNERS @@ -0,0 +1,3 @@ +set noparent +szager@chromium.org +cmp@chromium.org diff --git a/engine/src/flutter/build/git-hooks/pre-commit b/engine/src/flutter/build/git-hooks/pre-commit new file mode 100755 index 0000000000..41b596344c --- /dev/null +++ b/engine/src/flutter/build/git-hooks/pre-commit @@ -0,0 +1,60 @@ +#!/bin/sh + +submodule_diff() { + if test -n "$2"; then + git diff-tree -r --ignore-submodules=dirty "$1" "$2" | grep -e '^:160000' -e '^:...... 160000' | xargs + else + git diff-index --cached --ignore-submodules=dirty "$1" | grep -e '^:160000' -e '^:...... 160000' | xargs + fi +} + +if git rev-parse --verify --quiet --no-revs MERGE_HEAD; then + merge_base=$(git merge-base HEAD MERGE_HEAD) + if test -z "$(submodule_diff $merge_base HEAD)"; then + # Most up-to-date submodules are in MERGE_HEAD. + head_ref=MERGE_HEAD + else + # Most up-to-date submodules are in HEAD. + head_ref=HEAD + fi +else + # No merge in progress. Submodules must match HEAD. + head_ref=HEAD +fi + +submods=$(submodule_diff $head_ref) +if test "$submods"; then + echo "You are trying to commit changes to the following submodules:" 1>&2 + echo 1>&2 + echo $submods | cut -d ' ' -f 6 | sed 's/^/ /g' 1>&2 + cat <&2 + +Submodule commits are not allowed. Please run: + + git status --ignore-submodules=dirty + +and/or: + + git diff-index --cached --ignore-submodules=dirty HEAD + +... to see what's in your index. + +If you're really and truly trying to roll the version of a submodule, you should +commit the new version to DEPS, instead. +EOF + exit 1 +fi + +gitmodules_diff() { + git diff-index --cached "$1" .gitmodules +} + +if [ "$(git ls-files .gitmodules)" ] && [ "$(gitmodules_diff $head_ref)" ]; then + cat <&2 +You are trying to commit a change to .gitmodules. That is not allowed. +To make changes to submodule names/paths, edit DEPS. +EOF + exit 1 +fi + +exit 0 diff --git a/engine/src/flutter/build/gn_helpers.py b/engine/src/flutter/build/gn_helpers.py new file mode 100644 index 0000000000..3b0647d9a5 --- /dev/null +++ b/engine/src/flutter/build/gn_helpers.py @@ -0,0 +1,39 @@ +# Copyright 2014 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. + +"""Helper functions useful when writing scripts that are run from GN's +exec_script function.""" + +class GNException(Exception): + pass + + +def ToGNString(value, allow_dicts = True): + """Prints the given value to stdout. + + allow_dicts indicates if this function will allow converting dictionaries + to GN scopes. This is only possible at the top level, you can't nest a + GN scope in a list, so this should be set to False for recursive calls.""" + if isinstance(value, str): + if value.find('\n') >= 0: + raise GNException("Trying to print a string with a newline in it.") + return '"' + value.replace('"', '\\"') + '"' + + if isinstance(value, list): + return '[ %s ]' % ', '.join(ToGNString(v) for v in value) + + if isinstance(value, dict): + if not allow_dicts: + raise GNException("Attempting to recursively print a dictionary.") + result = "" + for key in value: + if not isinstance(key, str): + raise GNException("Dictionary key is not a string.") + result += "%s = %s\n" % (key, ToGNString(value[key], False)) + return result + + if isinstance(value, int): + return str(value) + + raise GNException("Unsupported type when printing to GN.") diff --git a/engine/src/flutter/build/gn_run_binary.py b/engine/src/flutter/build/gn_run_binary.py new file mode 100644 index 0000000000..7d83f6136f --- /dev/null +++ b/engine/src/flutter/build/gn_run_binary.py @@ -0,0 +1,22 @@ +# Copyright 2014 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. + +"""Helper script for GN to run an arbitrary binary. See compiled_action.gni. + +Run with: + python gn_run_binary.py [args ...] +""" + +import sys +import subprocess + +# This script is designed to run binaries produced by the current build. We +# always prefix it with "./" to avoid picking up system versions that might +# also be on the path. +path = './' + sys.argv[1] + +# The rest of the arguements are passed directly to the executable. +args = [path] + sys.argv[2:] + +sys.exit(subprocess.call(args)) diff --git a/engine/src/flutter/build/gyp_chromium b/engine/src/flutter/build/gyp_chromium new file mode 100755 index 0000000000..9dac8710ff --- /dev/null +++ b/engine/src/flutter/build/gyp_chromium @@ -0,0 +1,333 @@ +#!/usr/bin/env python + +# Copyright (c) 2012 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. + +# This script is wrapper for Chromium that adds some support for how GYP +# is invoked by Chromium beyond what can be done in the gclient hooks. + +import argparse +import glob +import gyp_environment +import os +import re +import shlex +import subprocess +import string +import sys +import vs_toolchain + +script_dir = os.path.dirname(os.path.realpath(__file__)) +chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir)) + +sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib')) +import gyp + +# Assume this file is in a one-level-deep subdirectory of the source root. +SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Add paths so that pymod_do_main(...) can import files. +sys.path.insert(1, os.path.join(chrome_src, 'android_webview', 'tools')) +sys.path.insert(1, os.path.join(chrome_src, 'build', 'android', 'gyp')) +sys.path.insert(1, os.path.join(chrome_src, 'chrome', 'tools', 'build')) +sys.path.insert(1, os.path.join(chrome_src, 'chromecast', 'tools', 'build')) +sys.path.insert(1, os.path.join(chrome_src, 'ios', 'chrome', 'tools', 'build')) +sys.path.insert(1, os.path.join(chrome_src, 'native_client', 'build')) +sys.path.insert(1, os.path.join(chrome_src, 'native_client_sdk', 'src', + 'build_tools')) +sys.path.insert(1, os.path.join(chrome_src, 'remoting', 'tools', 'build')) +sys.path.insert(1, os.path.join(chrome_src, 'third_party', 'liblouis')) +sys.path.insert(1, os.path.join(chrome_src, 'third_party', 'WebKit', + 'Source', 'build', 'scripts')) +sys.path.insert(1, os.path.join(chrome_src, 'tools')) +sys.path.insert(1, os.path.join(chrome_src, 'tools', 'generate_shim_headers')) +sys.path.insert(1, os.path.join(chrome_src, 'tools', 'grit')) + +# On Windows, Psyco shortens warm runs of build/gyp_chromium by about +# 20 seconds on a z600 machine with 12 GB of RAM, from 90 down to 70 +# seconds. Conversely, memory usage of build/gyp_chromium with Psyco +# maxes out at about 158 MB vs. 132 MB without it. +# +# Psyco uses native libraries, so we need to load a different +# installation depending on which OS we are running under. It has not +# been tested whether using Psyco on our Mac and Linux builds is worth +# it (the GYP running time is a lot shorter, so the JIT startup cost +# may not be worth it). +if sys.platform == 'win32': + try: + sys.path.insert(0, os.path.join(chrome_src, 'third_party', 'psyco_win32')) + import psyco + except: + psyco = None +else: + psyco = None + + +def GetSupplementalFiles(): + """Returns a list of the supplemental files that are included in all GYP + sources.""" + return glob.glob(os.path.join(chrome_src, '*', 'supplement.gypi')) + + +def ProcessGypDefinesItems(items): + """Converts a list of strings to a list of key-value pairs.""" + result = [] + for item in items: + tokens = item.split('=', 1) + # Some GYP variables have hyphens, which we don't support. + if len(tokens) == 2: + result += [(tokens[0], tokens[1])] + else: + # No value supplied, treat it as a boolean and set it. Note that we + # use the string '1' here so we have a consistent definition whether + # you do 'foo=1' or 'foo'. + result += [(tokens[0], '1')] + return result + + +def GetGypVars(supplemental_files): + """Returns a dictionary of all GYP vars.""" + # Find the .gyp directory in the user's home directory. + home_dot_gyp = os.environ.get('GYP_CONFIG_DIR', None) + if home_dot_gyp: + home_dot_gyp = os.path.expanduser(home_dot_gyp) + if not home_dot_gyp: + home_vars = ['HOME'] + if sys.platform in ('cygwin', 'win32'): + home_vars.append('USERPROFILE') + for home_var in home_vars: + home = os.getenv(home_var) + if home != None: + home_dot_gyp = os.path.join(home, '.gyp') + if not os.path.exists(home_dot_gyp): + home_dot_gyp = None + else: + break + + if home_dot_gyp: + include_gypi = os.path.join(home_dot_gyp, "include.gypi") + if os.path.exists(include_gypi): + supplemental_files += [include_gypi] + + # GYP defines from the supplemental.gypi files. + supp_items = [] + for supplement in supplemental_files: + with open(supplement, 'r') as f: + try: + file_data = eval(f.read(), {'__builtins__': None}, None) + except SyntaxError, e: + e.filename = os.path.abspath(supplement) + raise + variables = file_data.get('variables', []) + for v in variables: + supp_items += [(v, str(variables[v]))] + + # GYP defines from the environment. + env_items = ProcessGypDefinesItems( + shlex.split(os.environ.get('GYP_DEFINES', ''))) + + # GYP defines from the command line. + parser = argparse.ArgumentParser() + parser.add_argument('-D', dest='defines', action='append', default=[]) + cmdline_input_items = parser.parse_known_args()[0].defines + cmdline_items = ProcessGypDefinesItems(cmdline_input_items) + + vars_dict = dict(supp_items + env_items + cmdline_items) + return vars_dict + + +def GetOutputDirectory(): + """Returns the output directory that GYP will use.""" + + # Handle command line generator flags. + parser = argparse.ArgumentParser() + parser.add_argument('-G', dest='genflags', default=[], action='append') + genflags = parser.parse_known_args()[0].genflags + + # Handle generator flags from the environment. + genflags += shlex.split(os.environ.get('GYP_GENERATOR_FLAGS', '')) + + needle = 'output_dir=' + for item in genflags: + if item.startswith(needle): + return item[len(needle):] + + return 'out' + + +def additional_include_files(supplemental_files, args=[]): + """ + Returns a list of additional (.gypi) files to include, without duplicating + ones that are already specified on the command line. The list of supplemental + include files is passed in as an argument. + """ + # Determine the include files specified on the command line. + # This doesn't cover all the different option formats you can use, + # but it's mainly intended to avoid duplicating flags on the automatic + # makefile regeneration which only uses this format. + specified_includes = set() + for arg in args: + if arg.startswith('-I') and len(arg) > 2: + specified_includes.add(os.path.realpath(arg[2:])) + + result = [] + def AddInclude(path): + if os.path.realpath(path) not in specified_includes: + result.append(path) + + if os.environ.get('GYP_INCLUDE_FIRST') != None: + AddInclude(os.path.join(chrome_src, os.environ.get('GYP_INCLUDE_FIRST'))) + + # Always include common.gypi. + AddInclude(os.path.join(script_dir, 'common.gypi')) + + # Optionally add supplemental .gypi files if present. + for supplement in supplemental_files: + AddInclude(supplement) + + if os.environ.get('GYP_INCLUDE_LAST') != None: + AddInclude(os.path.join(chrome_src, os.environ.get('GYP_INCLUDE_LAST'))) + + return result + + +if __name__ == '__main__': + # Disabling garbage collection saves about 1 second out of 16 on a Linux + # z620 workstation. Since this is a short-lived process it's not a problem to + # leak a few cyclyc references in order to spare the CPU cycles for + # scanning the heap. + import gc + gc.disable() + + args = sys.argv[1:] + + use_analyzer = len(args) and args[0] == '--analyzer' + if use_analyzer: + args.pop(0) + os.environ['GYP_GENERATORS'] = 'analyzer' + args.append('-Gconfig_path=' + args.pop(0)) + args.append('-Ganalyzer_output_path=' + args.pop(0)) + + if int(os.environ.get('GYP_CHROMIUM_NO_ACTION', 0)): + print 'Skipping gyp_chromium due to GYP_CHROMIUM_NO_ACTION env var.' + sys.exit(0) + + # Use the Psyco JIT if available. + if psyco: + psyco.profile() + print "Enabled Psyco JIT." + + # Fall back on hermetic python if we happen to get run under cygwin. + # TODO(bradnelson): take this out once this issue is fixed: + # http://code.google.com/p/gyp/issues/detail?id=177 + if sys.platform == 'cygwin': + import find_depot_tools + depot_tools_path = find_depot_tools.add_depot_tools_to_path() + python_dir = sorted(glob.glob(os.path.join(depot_tools_path, + 'python2*_bin')))[-1] + env = os.environ.copy() + env['PATH'] = python_dir + os.pathsep + env.get('PATH', '') + cmd = [os.path.join(python_dir, 'python.exe')] + sys.argv + sys.exit(subprocess.call(cmd, env=env)) + + # This could give false positives since it doesn't actually do real option + # parsing. Oh well. + gyp_file_specified = any(arg.endswith('.gyp') for arg in args) + + gyp_environment.SetEnvironment() + + # If we didn't get a file, check an env var, and then fall back to + # assuming 'all.gyp' from the same directory as the script. + if not gyp_file_specified: + gyp_file = os.environ.get('CHROMIUM_GYP_FILE') + if gyp_file: + # Note that CHROMIUM_GYP_FILE values can't have backslashes as + # path separators even on Windows due to the use of shlex.split(). + args.extend(shlex.split(gyp_file)) + else: + args.append(os.path.join(script_dir, 'all.gyp')) + + supplemental_includes = GetSupplementalFiles() + gyp_vars_dict = GetGypVars(supplemental_includes) + # There shouldn't be a circular dependency relationship between .gyp files, + # but in Chromium's .gyp files, on non-Mac platforms, circular relationships + # currently exist. The check for circular dependencies is currently + # bypassed on other platforms, but is left enabled on iOS, where a violation + # of the rule causes Xcode to misbehave badly. + # TODO(mark): Find and kill remaining circular dependencies, and remove this + # option. http://crbug.com/35878. + # TODO(tc): Fix circular dependencies in ChromiumOS then add linux2 to the + # list. + if gyp_vars_dict.get('OS') != 'ios': + args.append('--no-circular-check') + + # libtool on Mac warns about duplicate basenames in static libraries, so + # they're disallowed in general by gyp. We are lax on this point, so disable + # this check other than on Mac. GN does not use static libraries as heavily, + # so over time this restriction will mostly go away anyway, even on Mac. + # https://code.google.com/p/gyp/issues/detail?id=384 + if sys.platform != 'darwin': + args.append('--no-duplicate-basename-check') + + # We explicitly don't support the make gyp generator (crbug.com/348686). Be + # nice and fail here, rather than choking in gyp. + if re.search(r'(^|,|\s)make($|,|\s)', os.environ.get('GYP_GENERATORS', '')): + print 'Error: make gyp generator not supported (check GYP_GENERATORS).' + sys.exit(1) + + # We explicitly don't support the native msvs gyp generator. Be nice and + # fail here, rather than generating broken projects. + if re.search(r'(^|,|\s)msvs($|,|\s)', os.environ.get('GYP_GENERATORS', '')): + print 'Error: msvs gyp generator not supported (check GYP_GENERATORS).' + print 'Did you mean to use the `msvs-ninja` generator?' + sys.exit(1) + + # If CHROMIUM_GYP_SYNTAX_CHECK is set to 1, it will invoke gyp with --check + # to enfore syntax checking. + syntax_check = os.environ.get('CHROMIUM_GYP_SYNTAX_CHECK') + if syntax_check and int(syntax_check): + args.append('--check') + + # TODO(dmikurube): Remove these checks and messages after a while. + if ('linux_use_tcmalloc' in gyp_vars_dict or + 'android_use_tcmalloc' in gyp_vars_dict): + print '*****************************************************************' + print '"linux_use_tcmalloc" and "android_use_tcmalloc" are deprecated!' + print '-----------------------------------------------------------------' + print 'You specify "linux_use_tcmalloc" or "android_use_tcmalloc" in' + print 'your GYP_DEFINES. Please switch them into "use_allocator" now.' + print 'See http://crbug.com/345554 for the details.' + print '*****************************************************************' + + # Automatically turn on crosscompile support for platforms that need it. + # (The Chrome OS build sets CC_host / CC_target which implicitly enables + # this mode.) + if all(('ninja' in os.environ.get('GYP_GENERATORS', ''), + gyp_vars_dict.get('OS') in ['android', 'ios'], + 'GYP_CROSSCOMPILE' not in os.environ)): + os.environ['GYP_CROSSCOMPILE'] = '1' + if gyp_vars_dict.get('OS') == 'android': + args.append('--check') + + args.extend( + ['-I' + i for i in additional_include_files(supplemental_includes, args)]) + + args.extend(['-D', 'gyp_output_dir=' + GetOutputDirectory()]) + + if not use_analyzer: + print 'Updating projects from gyp files...' + sys.stdout.flush() + + # Off we go... + gyp_rc = gyp.main(args) + + if not use_analyzer: + vs2013_runtime_dll_dirs = vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs() + if vs2013_runtime_dll_dirs: + x64_runtime, x86_runtime = vs2013_runtime_dll_dirs + vs_toolchain.CopyVsRuntimeDlls( + os.path.join(chrome_src, GetOutputDirectory()), + (x86_runtime, x64_runtime)) + + sys.exit(gyp_rc) diff --git a/engine/src/flutter/build/gyp_chromium.py b/engine/src/flutter/build/gyp_chromium.py new file mode 100644 index 0000000000..f9e8ac8ed8 --- /dev/null +++ b/engine/src/flutter/build/gyp_chromium.py @@ -0,0 +1,18 @@ +# Copyright 2012 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. + +# This file is (possibly, depending on python version) imported by +# gyp_chromium when GYP_PARALLEL=1 and it creates sub-processes +# through the multiprocessing library. + +# Importing in Python 2.6 (fixed in 2.7) on Windows doesn't search for +# imports that don't end in .py (and aren't directories with an +# __init__.py). This wrapper makes "import gyp_chromium" work with +# those old versions and makes it possible to execute gyp_chromium.py +# directly on Windows where the extension is useful. + +import os + +path = os.path.abspath(os.path.split(__file__)[0]) +execfile(os.path.join(path, 'gyp_chromium')) diff --git a/engine/src/flutter/build/gyp_chromium_test.py.remove b/engine/src/flutter/build/gyp_chromium_test.py.remove new file mode 100755 index 0000000000..0c0e479d1c --- /dev/null +++ b/engine/src/flutter/build/gyp_chromium_test.py.remove @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import os +import sys +import unittest + +SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) +SRC_DIR = os.path.dirname(SCRIPT_DIR) + +sys.path.append(os.path.join(SRC_DIR, 'third_party', 'pymock')) + +import mock + +# TODO(sbc): Make gyp_chromium more testable by putting the code in +# a .py file. +gyp_chromium = __import__('gyp_chromium') + + +class TestGetOutputDirectory(unittest.TestCase): + @mock.patch('os.environ', {}) + @mock.patch('sys.argv', [__file__]) + def testDefaultValue(self): + self.assertEqual(gyp_chromium.GetOutputDirectory(), 'out') + + @mock.patch('os.environ', {'GYP_GENERATOR_FLAGS': 'output_dir=envfoo'}) + @mock.patch('sys.argv', [__file__]) + def testEnvironment(self): + self.assertEqual(gyp_chromium.GetOutputDirectory(), 'envfoo') + + @mock.patch('os.environ', {'GYP_GENERATOR_FLAGS': 'output_dir=envfoo'}) + @mock.patch('sys.argv', [__file__, '-Goutput_dir=cmdfoo']) + def testGFlagOverridesEnv(self): + self.assertEqual(gyp_chromium.GetOutputDirectory(), 'cmdfoo') + + @mock.patch('os.environ', {}) + @mock.patch('sys.argv', [__file__, '-G', 'output_dir=foo']) + def testGFlagWithSpace(self): + self.assertEqual(gyp_chromium.GetOutputDirectory(), 'foo') + + +class TestGetGypVars(unittest.TestCase): + @mock.patch('os.environ', {}) + def testDefault(self): + self.assertEqual(gyp_chromium.GetGypVars([]), {}) + + @mock.patch('os.environ', {}) + @mock.patch('sys.argv', [__file__, '-D', 'foo=bar']) + def testDFlags(self): + self.assertEqual(gyp_chromium.GetGypVars([]), {'foo': 'bar'}) + + @mock.patch('os.environ', {}) + @mock.patch('sys.argv', [__file__, '-D', 'foo']) + def testDFlagsNoValue(self): + self.assertEqual(gyp_chromium.GetGypVars([]), {'foo': '1'}) + + @mock.patch('os.environ', {}) + @mock.patch('sys.argv', [__file__, '-D', 'foo=bar', '-Dbaz']) + def testDFlagMulti(self): + self.assertEqual(gyp_chromium.GetGypVars([]), {'foo': 'bar', 'baz': '1'}) + + +if __name__ == '__main__': + unittest.main() diff --git a/engine/src/flutter/build/gyp_environment.py b/engine/src/flutter/build/gyp_environment.py new file mode 100644 index 0000000000..fb50645d56 --- /dev/null +++ b/engine/src/flutter/build/gyp_environment.py @@ -0,0 +1,33 @@ +# Copyright 2014 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. + +""" +Sets up various automatic gyp environment variables. These are used by +gyp_chromium and landmines.py which run at different stages of runhooks. To +make sure settings are consistent between them, all setup should happen here. +""" + +import gyp_helper +import os +import sys +import vs_toolchain + +def SetEnvironment(): + """Sets defaults for GYP_* variables.""" + gyp_helper.apply_chromium_gyp_env() + + # Default to ninja on linux and windows, but only if no generator has + # explicitly been set. + # Also default to ninja on mac, but only when not building chrome/ios. + # . -f / --format has precedence over the env var, no need to check for it + # . set the env var only if it hasn't been set yet + # . chromium.gyp_env has been applied to os.environ at this point already + if sys.platform.startswith(('linux', 'win', 'freebsd')) and \ + not os.environ.get('GYP_GENERATORS'): + os.environ['GYP_GENERATORS'] = 'ninja' + elif sys.platform == 'darwin' and not os.environ.get('GYP_GENERATORS') and \ + not 'OS=ios' in os.environ.get('GYP_DEFINES', []): + os.environ['GYP_GENERATORS'] = 'ninja' + + vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs() diff --git a/engine/src/flutter/build/gyp_helper.py b/engine/src/flutter/build/gyp_helper.py new file mode 100644 index 0000000000..c840f2d6dc --- /dev/null +++ b/engine/src/flutter/build/gyp_helper.py @@ -0,0 +1,68 @@ +# Copyright (c) 2012 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. + +# This file helps gyp_chromium and landmines correctly set up the gyp +# environment from chromium.gyp_env on disk + +import os + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +CHROME_SRC = os.path.dirname(SCRIPT_DIR) + + +def apply_gyp_environment_from_file(file_path): + """Reads in a *.gyp_env file and applies the valid keys to os.environ.""" + if not os.path.exists(file_path): + return + with open(file_path, 'rU') as f: + file_contents = f.read() + try: + file_data = eval(file_contents, {'__builtins__': None}, None) + except SyntaxError, e: + e.filename = os.path.abspath(file_path) + raise + supported_vars = ( + 'CC', + 'CC_wrapper', + 'CC.host_wrapper', + 'CHROMIUM_GYP_FILE', + 'CHROMIUM_GYP_SYNTAX_CHECK', + 'CXX', + 'CXX_wrapper', + 'CXX.host_wrapper', + 'GYP_DEFINES', + 'GYP_GENERATOR_FLAGS', + 'GYP_CROSSCOMPILE', + 'GYP_GENERATOR_OUTPUT', + 'GYP_GENERATORS', + 'GYP_INCLUDE_FIRST', + 'GYP_INCLUDE_LAST', + 'GYP_MSVS_VERSION', + ) + for var in supported_vars: + file_val = file_data.get(var) + if file_val: + if var in os.environ: + behavior = 'replaces' + if var == 'GYP_DEFINES': + result = file_val + ' ' + os.environ[var] + behavior = 'merges with, and individual components override,' + else: + result = os.environ[var] + print 'INFO: Environment value for "%s" %s value in %s' % ( + var, behavior, os.path.abspath(file_path) + ) + string_padding = max(len(var), len(file_path), len('result')) + print ' %s: %s' % (var.rjust(string_padding), os.environ[var]) + print ' %s: %s' % (file_path.rjust(string_padding), file_val) + os.environ[var] = result + else: + os.environ[var] = file_val + + +def apply_chromium_gyp_env(): + if 'SKIP_CHROMIUM_GYP_ENV' not in os.environ: + # Update the environment based on chromium.gyp_env + path = os.path.join(os.path.dirname(CHROME_SRC), 'chromium.gyp_env') + apply_gyp_environment_from_file(path) diff --git a/engine/src/flutter/build/gypi_to_gn.py b/engine/src/flutter/build/gypi_to_gn.py new file mode 100644 index 0000000000..a107f94fca --- /dev/null +++ b/engine/src/flutter/build/gypi_to_gn.py @@ -0,0 +1,167 @@ +# Copyright 2014 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. + +"""Converts a given gypi file to a python scope and writes the result to stdout. + +It is assumed that the file contains a toplevel dictionary, and this script +will return that dictionary as a GN "scope" (see example below). This script +does not know anything about GYP and it will not expand variables or execute +conditions. + +It will strip conditions blocks. + +A variables block at the top level will be flattened so that the variables +appear in the root dictionary. This way they can be returned to the GN code. + +Say your_file.gypi looked like this: + { + 'sources': [ 'a.cc', 'b.cc' ], + 'defines': [ 'ENABLE_DOOM_MELON' ], + } + +You would call it like this: + gypi_values = exec_script("//build/gypi_to_gn.py", + [ rebase_path("your_file.gypi") ], + "scope", + [ "your_file.gypi" ]) + +Notes: + - The rebase_path call converts the gypi file from being relative to the + current build file to being system absolute for calling the script, which + will have a different current directory than this file. + + - The "scope" parameter tells GN to interpret the result as a series of GN + variable assignments. + + - The last file argument to exec_script tells GN that the given file is a + dependency of the build so Ninja can automatically re-run GN if the file + changes. + +Read the values into a target like this: + component("mycomponent") { + sources = gypi_values.sources + defines = gypi_values.defines + } + +Sometimes your .gypi file will include paths relative to a different +directory than the current .gn file. In this case, you can rebase them to +be relative to the current directory. + sources = rebase_path(gypi_values.sources, ".", + "//path/gypi/input/values/are/relative/to") + +This script will tolerate a 'variables' in the toplevel dictionary or not. If +the toplevel dictionary just contains one item called 'variables', it will be +collapsed away and the result will be the contents of that dictinoary. Some +.gypi files are written with or without this, depending on how they expect to +be embedded into a .gyp file. + +This script also has the ability to replace certain substrings in the input. +Generally this is used to emulate GYP variable expansion. If you passed the +argument "--replace=<(foo)=bar" then all instances of "<(foo)" in strings in +the input will be replaced with "bar": + + gypi_values = exec_script("//build/gypi_to_gn.py", + [ rebase_path("your_file.gypi"), + "--replace=<(foo)=bar"], + "scope", + [ "your_file.gypi" ]) + +""" + +import gn_helpers +from optparse import OptionParser +import sys + +def LoadPythonDictionary(path): + file_string = open(path).read() + try: + file_data = eval(file_string, {'__builtins__': None}, None) + except SyntaxError, e: + e.filename = path + raise + except Exception, e: + raise Exception("Unexpected error while reading %s: %s" % (path, str(e))) + + assert isinstance(file_data, dict), "%s does not eval to a dictionary" % path + + # Flatten any variables to the top level. + if 'variables' in file_data: + file_data.update(file_data['variables']) + del file_data['variables'] + + # Strip any conditions. + if 'conditions' in file_data: + del file_data['conditions'] + if 'target_conditions' in file_data: + del file_data['target_conditions'] + + # Strip targets in the toplevel, since some files define these and we can't + # slurp them in. + if 'targets' in file_data: + del file_data['targets'] + + return file_data + + +def ReplaceSubstrings(values, search_for, replace_with): + """Recursively replaces substrings in a value. + + Replaces all substrings of the "search_for" with "repace_with" for all + strings occurring in "values". This is done by recursively iterating into + lists as well as the keys and values of dictionaries.""" + if isinstance(values, str): + return values.replace(search_for, replace_with) + + if isinstance(values, list): + return [ReplaceSubstrings(v, search_for, replace_with) for v in values] + + if isinstance(values, dict): + # For dictionaries, do the search for both the key and values. + result = {} + for key, value in values.items(): + new_key = ReplaceSubstrings(key, search_for, replace_with) + new_value = ReplaceSubstrings(value, search_for, replace_with) + result[new_key] = new_value + return result + + # Assume everything else is unchanged. + return values + +def main(): + parser = OptionParser() + parser.add_option("-r", "--replace", action="append", + help="Replaces substrings. If passed a=b, replaces all substrs a with b.") + (options, args) = parser.parse_args() + + if len(args) != 1: + raise Exception("Need one argument which is the .gypi file to read.") + + data = LoadPythonDictionary(args[0]) + if options.replace: + # Do replacements for all specified patterns. + for replace in options.replace: + split = replace.split('=') + # Allow "foo=" to replace with nothing. + if len(split) == 1: + split.append('') + assert len(split) == 2, "Replacement must be of the form 'key=value'." + data = ReplaceSubstrings(data, split[0], split[1]) + + # Sometimes .gypi files use the GYP syntax with percents at the end of the + # variable name (to indicate not to overwrite a previously-defined value): + # 'foo%': 'bar', + # Convert these to regular variables. + for key in data: + if len(key) > 1 and key[len(key) - 1] == '%': + data[key[:-1]] = data[key] + del data[key] + + print gn_helpers.ToGNString(data) + +if __name__ == '__main__': + try: + main() + except Exception, e: + print str(e) + sys.exit(1) diff --git a/engine/src/flutter/build/install-android-sdks.sh b/engine/src/flutter/build/install-android-sdks.sh new file mode 100755 index 0000000000..5c4edafec5 --- /dev/null +++ b/engine/src/flutter/build/install-android-sdks.sh @@ -0,0 +1,25 @@ +#!/bin/bash -e + +# Copyright (c) 2012 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. + +# Script to install SDKs needed to build chromium on android. +# See http://code.google.com/p/chromium/wiki/AndroidBuildInstructions + +echo 'checking for sdk packages install' +# Use absolute path to call 'android' so script can be run from any directory. +cwd=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +# Get the SDK extras packages to install from the DEPS file 'sdkextras' hook. +packages="$(python ${cwd}/get_sdk_extras_packages.py)" +for package in "${packages}"; do + pkg_id=$(${cwd}/../third_party/android_tools/sdk/tools/android list sdk | \ + grep -i "$package," | \ + awk '/^[ ]*[0-9]*- / {gsub("-",""); print $1}') + if [[ -n ${pkg_id} ]]; then + ${cwd}/../third_party/android_tools/sdk/tools/android update sdk --no-ui \ + --filter ${pkg_id} + fi +done + +echo "install-android-sdks.sh complete." diff --git a/engine/src/flutter/build/install-build-deps-android.sh b/engine/src/flutter/build/install-build-deps-android.sh new file mode 100755 index 0000000000..cf87381113 --- /dev/null +++ b/engine/src/flutter/build/install-build-deps-android.sh @@ -0,0 +1,100 @@ +#!/bin/bash -e + +# Copyright (c) 2012 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. + +# Script to install everything needed to build chromium on android, including +# items requiring sudo privileges. +# See http://code.google.com/p/chromium/wiki/AndroidBuildInstructions + +# This script installs the sun-java6 packages (bin, jre and jdk). Sun requires +# a license agreement, so upon installation it will prompt the user. To get +# past the curses-based dialog press TAB TAB to agree. + +args="$@" +if test "$1" = "--skip-sdk-packages"; then + skip_inst_sdk_packages=1 + args="${@:2}" +else + skip_inst_sdk_packages=0 +fi + +if ! uname -m | egrep -q "i686|x86_64"; then + echo "Only x86 architectures are currently supported" >&2 + exit +fi + +# Install first the default Linux build deps. +"$(dirname "${BASH_SOURCE[0]}")/install-build-deps.sh" \ + --no-syms --lib32 --no-arm --no-chromeos-fonts --no-nacl --no-prompt "${args}" + +lsb_release=$(lsb_release --codename --short) + +# The temporary directory used to store output of update-java-alternatives +TEMPDIR=$(mktemp -d) +cleanup() { + local status=${?} + trap - EXIT + rm -rf "${TEMPDIR}" + exit ${status} +} +trap cleanup EXIT + +# Fix deps +sudo apt-get -f install + +# Install deps +# This step differs depending on what Ubuntu release we are running +# on since the package names are different, and Sun's Java must +# be installed manually on late-model versions. + +# common +sudo apt-get -y install lighttpd python-pexpect xvfb x11-utils + +# Some binaries in the Android SDK require 32-bit libraries on the host. +# See https://developer.android.com/sdk/installing/index.html?pkg=tools +if [[ $lsb_release == "precise" ]]; then + sudo apt-get -y install ia32-libs +else + sudo apt-get -y install libncurses5:i386 libstdc++6:i386 zlib1g:i386 +fi + +sudo apt-get -y install ant + +# Install openjdk and openjre 7 stuff +sudo apt-get -y install openjdk-7-jre openjdk-7-jdk + +# Switch version of Java to openjdk 7. +# Some Java plugins (e.g. for firefox, mozilla) are not required to build, and +# thus are treated only as warnings. Any errors in updating java alternatives +# which are not '*-javaplugin.so' will cause errors and stop the script from +# completing successfully. +if ! sudo update-java-alternatives -s java-1.7.0-openjdk-amd64 \ + >& "${TEMPDIR}"/update-java-alternatives.out +then + # Check that there are the expected javaplugin.so errors for the update + if grep 'javaplugin.so' "${TEMPDIR}"/update-java-alternatives.out >& \ + /dev/null + then + # Print as warnings all the javaplugin.so errors + echo 'WARNING: java-6-sun has no alternatives for the following plugins:' + grep 'javaplugin.so' "${TEMPDIR}"/update-java-alternatives.out + fi + # Check if there are any errors that are not javaplugin.so + if grep -v 'javaplugin.so' "${TEMPDIR}"/update-java-alternatives.out \ + >& /dev/null + then + # If there are non-javaplugin.so errors, treat as errors and exit + echo 'ERRORS: Failed to update alternatives for java-6-sun:' + grep -v 'javaplugin.so' "${TEMPDIR}"/update-java-alternatives.out + exit 1 + fi +fi + +# Install SDK packages for android +if test "$skip_inst_sdk_packages" != 1; then + "$(dirname "${BASH_SOURCE[0]}")/install-android-sdks.sh" +fi + +echo "install-build-deps-android.sh complete." diff --git a/engine/src/flutter/build/install-build-deps-mac.sh b/engine/src/flutter/build/install-build-deps-mac.sh new file mode 100755 index 0000000000..625ddf4a64 --- /dev/null +++ b/engine/src/flutter/build/install-build-deps-mac.sh @@ -0,0 +1,64 @@ +#!/bin/bash -e + +# Copyright (c) 2015 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. +# +# Script to install dependencies for developing Mojo on Mac OS X. + +XCODE_APP="/Applications/Xcode.app" +JDK_VERSION="1.7.0" + +echo + +INSTRUCTIONS="" + +if [ ! -d $XCODE_APP ]; then + INSTRUCTIONS="${INSTRUCTIONS}\n** Install the latest version of Xcode from the Mac App Store." +fi + +if [ -z $(which java) ] || ! java -version 2>&1 | grep -q ${JDK_VERSION}; then + temp=$( + echo "** Download the Java JDK (Java SE Development Kit) 7:" + echo " http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html" + ) + + INSTRUCTIONS="${INSTRUCTIONS}\n$temp" +fi + + +if [ -z $(which gclient) ] && [ ! -d $HOME/depot_tools ]; then + echo "*** Installing depot_tools in $HOME" + echo + cd $HOME + git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git + + temp=$( + echo "** Add this to your .bash_profile or similar:" + echo " export PATH=\"\${HOME}/depot_tools:\${PATH}\"" + ) + + INSTRUCTIONS="${INSTRUCTIONS}\n$temp" +fi + +if [ ! -z $(which brew) ]; then + brew install ant +elif [ ! -z $(which port) ]; then + sudo `which port` install apache-ant +else + INSTRUCTIONS="${INSTRUCTIONS}\n** Install homebrew (brew.sh) or macports (macports.org) and re-run this script." +fi + +sudo easy_install pip +sudo pip install requests + + +echo +echo "All done!" + +if [ ! -z "$INSTRUCTIONS" ]; then + echo + echo + echo "**** Follow these final instructions to get going." + echo $INSTRUCTIONS +fi diff --git a/engine/src/flutter/build/install-build-deps.sh b/engine/src/flutter/build/install-build-deps.sh new file mode 100755 index 0000000000..0c39d8709d --- /dev/null +++ b/engine/src/flutter/build/install-build-deps.sh @@ -0,0 +1,462 @@ +#!/bin/bash -e + +# Copyright (c) 2012 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. + +# Script to install everything needed to build chromium (well, ideally, anyway) +# See http://code.google.com/p/chromium/wiki/LinuxBuildInstructions +# and http://code.google.com/p/chromium/wiki/LinuxBuild64Bit + +usage() { + echo "Usage: $0 [--options]" + echo "Options:" + echo "--[no-]syms: enable or disable installation of debugging symbols" + echo "--lib32: enable installation of 32-bit libraries, e.g. for V8 snapshot" + echo "--[no-]arm: enable or disable installation of arm cross toolchain" + echo "--[no-]chromeos-fonts: enable or disable installation of Chrome OS"\ + "fonts" + echo "--[no-]nacl: enable or disable installation of prerequisites for"\ + "building standalone NaCl and all its toolchains" + echo "--no-prompt: silently select standard options/defaults" + echo "--quick-check: quickly try to determine if dependencies are installed" + echo " (this avoids interactive prompts and sudo commands," + echo " so might not be 100% accurate)" + echo "--unsupported: attempt installation even on unsupported systems" + echo "Script will prompt interactively if options not given." + exit 1 +} + +# Checks whether a particular package is available in the repos. +# USAGE: $ package_exists +package_exists() { + apt-cache pkgnames | grep -x "$1" > /dev/null 2>&1 +} + +# These default to on because (some) bots need them and it keeps things +# simple for the bot setup if all bots just run the script in its default +# mode. Developers who don't want stuff they don't need installed on their +# own workstations can pass --no-arm --no-nacl when running the script. +do_inst_arm=1 +do_inst_nacl=1 + +while test "$1" != "" +do + case "$1" in + --syms) do_inst_syms=1;; + --no-syms) do_inst_syms=0;; + --lib32) do_inst_lib32=1;; + --arm) do_inst_arm=1;; + --no-arm) do_inst_arm=0;; + --chromeos-fonts) do_inst_chromeos_fonts=1;; + --no-chromeos-fonts) do_inst_chromeos_fonts=0;; + --nacl) do_inst_nacl=1;; + --no-nacl) do_inst_nacl=0;; + --no-prompt) do_default=1 + do_quietly="-qq --assume-yes" + ;; + --quick-check) do_quick_check=1;; + --unsupported) do_unsupported=1;; + *) usage;; + esac + shift +done + +if test "$do_inst_arm" = "1"; then + do_inst_lib32=1 +fi + +# Check for lsb_release command in $PATH +if ! which lsb_release > /dev/null; then + echo "ERROR: lsb_release not found in \$PATH" >&2 + exit 1; +fi + +lsb_release=$(lsb_release --codename --short) +ubuntu_codenames="(precise|trusty|utopic|vivid)" +if [ 0 -eq "${do_unsupported-0}" ] && [ 0 -eq "${do_quick_check-0}" ] ; then + if [[ ! $lsb_release =~ $ubuntu_codenames ]]; then + echo "ERROR: Only Ubuntu 12.04 (precise), 14.04 (trusty), " \ + "14.10 (utopic) and 15.04 (vivid) are currently supported" >&2 + exit 1 + fi + + if ! uname -m | egrep -q "i686|x86_64"; then + echo "Only x86 architectures are currently supported" >&2 + exit + fi +fi + +if [ "x$(id -u)" != x0 ] && [ 0 -eq "${do_quick_check-0}" ]; then + echo "Running as non-root user." + echo "You might have to enter your password one or more times for 'sudo'." + echo +fi + +# Packages needed for chromeos only +chromeos_dev_list="libbluetooth-dev libxkbcommon-dev realpath" + +# Packages needed for development +dev_list="apache2.2-bin bison cdbs curl dpkg-dev elfutils devscripts fakeroot + flex fonts-thai-tlwg g++ git-core git-svn gperf language-pack-da + language-pack-fr language-pack-he language-pack-zh-hant + libapache2-mod-php5 libasound2-dev libbrlapi-dev libav-tools + libbz2-dev libcairo2-dev libcap-dev libcups2-dev libcurl4-gnutls-dev + libdrm-dev libelf-dev libexif-dev libgconf2-dev libglib2.0-dev + libglu1-mesa-dev libgnome-keyring-dev libgtk2.0-dev libkrb5-dev + libnspr4-dev libnss3-dev libpam0g-dev libpci-dev libpulse-dev + libsctp-dev libspeechd-dev libsqlite3-dev libssl-dev libudev-dev + libwww-perl libxslt1-dev libxss-dev libxt-dev libxtst-dev openbox + patch perl php5-cgi pkg-config python python-cherrypy3 python-crypto + python-dev python-numpy python-opencv python-openssl python-psutil + python-yaml rpm ruby subversion ttf-dejavu-core ttf-indic-fonts + ttf-kochi-gothic ttf-kochi-mincho wdiff xfonts-mathml zip + $chromeos_dev_list" + +# 64-bit systems need a minimum set of 32-bit compat packages for the pre-built +# NaCl binaries. +if file /sbin/init | grep -q 'ELF 64-bit'; then + dev_list="${dev_list} libc6-i386 lib32gcc1 lib32stdc++6" +fi + +# Run-time libraries required by chromeos only +chromeos_lib_list="libpulse0 libbz2-1.0" + +# Full list of required run-time libraries +lib_list="libatk1.0-0 libc6 libasound2 libcairo2 libcap2 libcups2 libexpat1 + libexif12 libfontconfig1 libfreetype6 libglib2.0-0 libgnome-keyring0 + libgtk2.0-0 libpam0g libpango1.0-0 libpci3 libpcre3 libpixman-1-0 + libpng12-0 libspeechd2 libstdc++6 libsqlite3-0 libx11-6 + libxau6 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxdmcp6 + libxext6 libxfixes3 libxi6 libxinerama1 libxrandr2 libxrender1 + libxtst6 zlib1g $chromeos_lib_list" + +# Debugging symbols for all of the run-time libraries +dbg_list="libatk1.0-dbg libc6-dbg libcairo2-dbg libfontconfig1-dbg + libglib2.0-0-dbg libgtk2.0-0-dbg libpango1.0-0-dbg libpcre3-dbg + libpixman-1-0-dbg libsqlite3-0-dbg libx11-6-dbg libxau6-dbg + libxcb1-dbg libxcomposite1-dbg libxcursor1-dbg libxdamage1-dbg + libxdmcp6-dbg libxext6-dbg libxfixes3-dbg libxi6-dbg libxinerama1-dbg + libxrandr2-dbg libxrender1-dbg libxtst6-dbg zlib1g-dbg" + +# Find the proper version of libstdc++6-4.x-dbg. +if [ "x$lsb_release" = "xprecise" ]; then + dbg_list="${dbg_list} libstdc++6-4.6-dbg" +elif [ "x$lsb_release" = "xtrusty" ]; then + dbg_list="${dbg_list} libstdc++6-4.8-dbg" +else + dbg_list="${dbg_list} libstdc++6-4.9-dbg" +fi + +# 32-bit libraries needed e.g. to compile V8 snapshot for Android or armhf +lib32_list="linux-libc-dev:i386" + +# arm cross toolchain packages needed to build chrome on armhf +arm_list="libc6-dev-armhf-cross + linux-libc-dev-armhf-cross + g++-arm-linux-gnueabihf" + +# Work around for dependency issue Ubuntu/Trusty: http://crbug.com/435056 +if [ "x$lsb_release" = "xtrusty" ]; then + arm_list+=" g++-4.8-multilib-arm-linux-gnueabihf + gcc-4.8-multilib-arm-linux-gnueabihf" +fi + +# Packages to build NaCl, its toolchains, and its ports. +naclports_list="ant autoconf bison cmake gawk intltool xutils-dev xsltproc" +nacl_list="g++-mingw-w64-i686 lib32z1-dev + libasound2:i386 libcap2:i386 libelf-dev:i386 libexif12:i386 + libfontconfig1:i386 libgconf-2-4:i386 libglib2.0-0:i386 libgpm2:i386 + libgtk2.0-0:i386 libncurses5:i386 lib32ncurses5-dev + libnss3:i386 libpango1.0-0:i386 + libssl1.0.0:i386 libtinfo-dev libtinfo-dev:i386 libtool + libxcomposite1:i386 libxcursor1:i386 libxdamage1:i386 libxi6:i386 + libxrandr2:i386 libxss1:i386 libxtst6:i386 texinfo xvfb + ${naclports_list}" + +# Find the proper version of libgbm-dev. We can't just install libgbm-dev as +# it depends on mesa, and only one version of mesa can exists on the system. +# Hence we must match the same version or this entire script will fail. +mesa_variant="" +for variant in "-lts-trusty" "-lts-utopic"; do + if $(dpkg-query -Wf'${Status}' libgl1-mesa-glx${variant} 2>/dev/null | \ + grep -q " ok installed"); then + mesa_variant="${variant}" + fi +done +dev_list="${dev_list} libgbm-dev${mesa_variant} + libgles2-mesa-dev${mesa_variant} libgl1-mesa-dev${mesa_variant} + mesa-common-dev${mesa_variant}" +nacl_list="${nacl_list} libgl1-mesa-glx${mesa_variant}:i386" + +# Some package names have changed over time +if package_exists ttf-mscorefonts-installer; then + dev_list="${dev_list} ttf-mscorefonts-installer" +else + dev_list="${dev_list} msttcorefonts" +fi +if package_exists libnspr4-dbg; then + dbg_list="${dbg_list} libnspr4-dbg libnss3-dbg" + lib_list="${lib_list} libnspr4 libnss3" +else + dbg_list="${dbg_list} libnspr4-0d-dbg libnss3-1d-dbg" + lib_list="${lib_list} libnspr4-0d libnss3-1d" +fi +if package_exists libjpeg-dev; then + dev_list="${dev_list} libjpeg-dev" +else + dev_list="${dev_list} libjpeg62-dev" +fi +if package_exists libudev1; then + dev_list="${dev_list} libudev1" + nacl_list="${nacl_list} libudev1:i386" +else + dev_list="${dev_list} libudev0" + nacl_list="${nacl_list} libudev0:i386" +fi +if package_exists libbrlapi0.6; then + dev_list="${dev_list} libbrlapi0.6" +else + dev_list="${dev_list} libbrlapi0.5" +fi + + +# Some packages are only needed if the distribution actually supports +# installing them. +if package_exists appmenu-gtk; then + lib_list="$lib_list appmenu-gtk" +fi + +# When cross building for arm/Android on 64-bit systems the host binaries +# that are part of v8 need to be compiled with -m32 which means +# that basic multilib support is needed. +if file /sbin/init | grep -q 'ELF 64-bit'; then + # gcc-multilib conflicts with the arm cross compiler (at least in trusty) but + # g++-X.Y-multilib gives us the 32-bit support that we need. Find out the + # appropriate value of X and Y by seeing what version the current + # distribution's g++-multilib package depends on. + multilib_package=$(apt-cache depends g++-multilib --important | \ + grep -E --color=never --only-matching '\bg\+\+-[0-9.]+-multilib\b') + lib32_list="$lib32_list $multilib_package" +fi + +# Waits for the user to press 'Y' or 'N'. Either uppercase of lowercase is +# accepted. Returns 0 for 'Y' and 1 for 'N'. If an optional parameter has +# been provided to yes_no(), the function also accepts RETURN as a user input. +# The parameter specifies the exit code that should be returned in that case. +# The function will echo the user's selection followed by a newline character. +# Users can abort the function by pressing CTRL-C. This will call "exit 1". +yes_no() { + if [ 0 -ne "${do_default-0}" ] ; then + [ $1 -eq 0 ] && echo "Y" || echo "N" + return $1 + fi + local c + while :; do + c="$(trap 'stty echo -iuclc icanon 2>/dev/null' EXIT INT TERM QUIT + stty -echo iuclc -icanon 2>/dev/null + dd count=1 bs=1 2>/dev/null | od -An -tx1)" + case "$c" in + " 0a") if [ -n "$1" ]; then + [ $1 -eq 0 ] && echo "Y" || echo "N" + return $1 + fi + ;; + " 79") echo "Y" + return 0 + ;; + " 6e") echo "N" + return 1 + ;; + "") echo "Aborted" >&2 + exit 1 + ;; + *) # The user pressed an unrecognized key. As we are not echoing + # any incorrect user input, alert the user by ringing the bell. + (tput bel) 2>/dev/null + ;; + esac + done +} + +if test "$do_inst_syms" = "" && test 0 -eq ${do_quick_check-0} +then + echo "This script installs all tools and libraries needed to build Chromium." + echo "" + echo "For most of the libraries, it can also install debugging symbols, which" + echo "will allow you to debug code in the system libraries. Most developers" + echo "won't need these symbols." + echo -n "Do you want me to install them for you (y/N) " + if yes_no 1; then + do_inst_syms=1 + fi +fi +if test "$do_inst_syms" = "1"; then + echo "Including debugging symbols." +else + echo "Skipping debugging symbols." + dbg_list= +fi + +if test "$do_inst_lib32" = "1" ; then + echo "Including 32-bit libraries for ARM/Android." +else + echo "Skipping 32-bit libraries for ARM/Android." + lib32_list= +fi + +if test "$do_inst_arm" = "1" ; then + echo "Including ARM cross toolchain." +else + echo "Skipping ARM cross toolchain." + arm_list= +fi + +if test "$do_inst_nacl" = "1"; then + echo "Including NaCl, NaCl toolchain, NaCl ports dependencies." +else + echo "Skipping NaCl, NaCl toolchain, NaCl ports dependencies." + nacl_list= +fi + +# The `sort -r -s -t: -k2` sorts all the :i386 packages to the front, to avoid +# confusing dpkg-query (crbug.com/446172). +packages="$( + echo "${dev_list} ${lib_list} ${dbg_list} ${lib32_list} ${arm_list}"\ + "${nacl_list}" | tr " " "\n" | sort -u | sort -r -s -t: -k2 | tr "\n" " " +)" + +if [ 1 -eq "${do_quick_check-0}" ] ; then + failed_check="$(dpkg-query -W -f '${PackageSpec}:${Status}\n' \ + ${packages} 2>&1 | grep -v "ok installed" || :)" + if [ -n "${failed_check}" ]; then + echo + nomatch="$(echo "${failed_check}" | \ + sed -e "s/^No packages found matching \(.*\).$/\1/;t;d")" + missing="$(echo "${failed_check}" | \ + sed -e "/^No packages found matching/d;s/^\(.*\):.*$/\1/")" + if [ "$nomatch" ]; then + # Distinguish between packages that actually aren't available to the + # system (i.e. not in any repo) and packages that just aren't known to + # dpkg (i.e. managed by apt). + unknown="" + for p in ${nomatch}; do + if apt-cache show ${p} > /dev/null 2>&1; then + missing="${p}\n${missing}" + else + unknown="${p}\n${unknown}" + fi + done + if [ -n "${unknown}" ]; then + echo "WARNING: The following packages are unknown to your system" + echo "(maybe missing a repo or need to 'sudo apt-get update'):" + echo -e "${unknown}" | sed -e "s/^/ /" + fi + fi + if [ -n "${missing}" ]; then + echo "WARNING: The following packages are not installed:" + echo -e "${missing}" | sed -e "s/^/ /" + fi + exit 1 + fi + exit 0 +fi + +if test "$do_inst_lib32" = "1" || test "$do_inst_nacl" = "1"; then + if [[ ! $lsb_release =~ (precise) ]]; then + sudo dpkg --add-architecture i386 + fi +fi +sudo apt-get update + +# We initially run "apt-get" with the --reinstall option and parse its output. +# This way, we can find all the packages that need to be newly installed +# without accidentally promoting any packages from "auto" to "manual". +# We then re-run "apt-get" with just the list of missing packages. +echo "Finding missing packages..." +# Intentionally leaving $packages unquoted so it's more readable. +echo "Packages required: " $packages +echo +new_list_cmd="sudo apt-get install --reinstall $(echo $packages)" +if new_list="$(yes n | LANGUAGE=en LANG=C $new_list_cmd)"; then + # We probably never hit this following line. + echo "No missing packages, and the packages are up-to-date." +elif [ $? -eq 1 ]; then + # We expect apt-get to have exit status of 1. + # This indicates that we cancelled the install with "yes n|". + new_list=$(echo "$new_list" | + sed -e '1,/The following NEW packages will be installed:/d;s/^ //;t;d') + new_list=$(echo "$new_list" | sed 's/ *$//') + if [ -z "$new_list" ] ; then + echo "No missing packages, and the packages are up-to-date." + else + echo "Installing missing packages: $new_list." + sudo apt-get install ${do_quietly-} ${new_list} + fi + echo +else + # An apt-get exit status of 100 indicates that a real error has occurred. + + # I am intentionally leaving out the '"'s around new_list_cmd, + # as this makes it easier to cut and paste the output + echo "The following command failed: " ${new_list_cmd} + echo + echo "It produces the following output:" + yes n | $new_list_cmd || true + echo + echo "You will have to install the above packages yourself." + echo + exit 100 +fi + +# Install the Chrome OS default fonts. This must go after running +# apt-get, since install-chromeos-fonts depends on curl. +if test "$do_inst_chromeos_fonts" != "0"; then + echo + echo "Installing Chrome OS fonts." + dir=`echo $0 | sed -r -e 's/\/[^/]+$//'` + if ! sudo $dir/linux/install-chromeos-fonts.py; then + echo "ERROR: The installation of the Chrome OS default fonts failed." + if [ `stat -f -c %T $dir` == "nfs" ]; then + echo "The reason is that your repo is installed on a remote file system." + else + echo "This is expected if your repo is installed on a remote file system." + fi + echo "It is recommended to install your repo on a local file system." + echo "You can skip the installation of the Chrome OS default founts with" + echo "the command line option: --no-chromeos-fonts." + exit 1 + fi +else + echo "Skipping installation of Chrome OS fonts." +fi + +# $1 - target name +# $2 - link name +create_library_symlink() { + target=$1 + linkname=$2 + if [ -L $linkname ]; then + if [ "$(basename $(readlink $linkname))" != "$(basename $target)" ]; then + sudo rm $linkname + fi + fi + if [ ! -r $linkname ]; then + echo "Creating link: $linkname" + sudo ln -fs $target $linkname + fi +} + +if test "$do_inst_nacl" = "1"; then + echo "Installing symbolic links for NaCl." + # naclports needs to cross build python for i386, but libssl1.0.0:i386 + # only contains libcrypto.so.1.0.0 and not the symlink needed for + # linking (libcrypto.so). + create_library_symlink /lib/i386-linux-gnu/libcrypto.so.1.0.0 \ + /usr/lib/i386-linux-gnu/libcrypto.so + + create_library_symlink /lib/i386-linux-gnu/libssl.so.1.0.0 \ + /usr/lib/i386-linux-gnu/libssl.so +else + echo "Skipping symbolic links for NaCl." +fi diff --git a/engine/src/flutter/build/install-chroot.sh b/engine/src/flutter/build/install-chroot.sh new file mode 100755 index 0000000000..e2d558b76c --- /dev/null +++ b/engine/src/flutter/build/install-chroot.sh @@ -0,0 +1,858 @@ +#!/bin/bash -e + +# Copyright (c) 2012 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. + +# This script installs Debian-derived distributions in a chroot environment. +# It can for example be used to have an accurate 32bit build and test +# environment when otherwise working on a 64bit machine. +# N. B. it is unlikely that this script will ever work on anything other than a +# Debian-derived system. + +# Older Debian based systems had both "admin" and "adm" groups, with "admin" +# apparently being used in more places. Newer distributions have standardized +# on just the "adm" group. Check /etc/group for the preferred name of the +# administrator group. +admin=$(grep '^admin:' /etc/group >&/dev/null && echo admin || echo adm) + +usage() { + echo "usage: ${0##*/} [-m mirror] [-g group,...] [-s] [-c]" + echo "-b dir additional directories that should be bind mounted," + echo ' or "NONE".' + echo " Default: if local filesystems present, ask user for help" + echo "-g group,... groups that can use the chroot unauthenticated" + echo " Default: '${admin}' and current user's group ('$(id -gn)')" + echo "-l List all installed chroot environments" + echo "-m mirror an alternate repository mirror for package downloads" + echo "-s configure default deb-srcs" + echo "-c always copy 64bit helper binaries to 32bit chroot" + echo "-h this help message" +} + +process_opts() { + local OPTNAME OPTIND OPTERR OPTARG + while getopts ":b:g:lm:sch" OPTNAME; do + case "$OPTNAME" in + b) + if [ "${OPTARG}" = "NONE" -a -z "${bind_mounts}" ]; then + bind_mounts="${OPTARG}" + else + if [ "${bind_mounts}" = "NONE" -o "${OPTARG}" = "${OPTARG#/}" -o \ + ! -d "${OPTARG}" ]; then + echo "Invalid -b option(s)" + usage + exit 1 + fi + bind_mounts="${bind_mounts} +${OPTARG} ${OPTARG} none rw,bind 0 0" + fi + ;; + g) + [ -n "${OPTARG}" ] && + chroot_groups="${chroot_groups}${chroot_groups:+,}${OPTARG}" + ;; + l) + list_all_chroots + exit + ;; + m) + if [ -n "${mirror}" ]; then + echo "You can only specify exactly one mirror location" + usage + exit 1 + fi + mirror="$OPTARG" + ;; + s) + add_srcs="y" + ;; + c) + copy_64="y" + ;; + h) + usage + exit 0 + ;; + \:) + echo "'-$OPTARG' needs an argument." + usage + exit 1 + ;; + *) + echo "invalid command-line option: $OPTARG" + usage + exit 1 + ;; + esac + done + + if [ $# -ge ${OPTIND} ]; then + eval echo "Unexpected command line argument: \${${OPTIND}}" + usage + exit 1 + fi +} + +list_all_chroots() { + for i in /var/lib/chroot/*; do + i="${i##*/}" + [ "${i}" = "*" ] && continue + [ -x "/usr/local/bin/${i%bit}" ] || continue + grep -qs "^\[${i%bit}\]\$" /etc/schroot/schroot.conf || continue + [ -r "/etc/schroot/script-${i}" -a \ + -r "/etc/schroot/mount-${i}" ] || continue + echo "${i%bit}" + done +} + +getkey() { + ( + trap 'stty echo -iuclc icanon 2>/dev/null' EXIT INT TERM QUIT HUP + stty -echo iuclc -icanon 2>/dev/null + dd count=1 bs=1 2>/dev/null + ) +} + +chr() { + printf "\\$(printf '%03o' "$1")" +} + +ord() { + printf '%d' $(printf '%c' "$1" | od -tu1 -An) +} + +is_network_drive() { + stat -c %T -f "$1/" 2>/dev/null | + egrep -qs '^nfs|cifs|smbfs' +} + +# Check that we are running as a regular user +[ "$(id -nu)" = root ] && { + echo "Run this script as a regular user and provide your \"sudo\"" \ + "password if requested" >&2 + exit 1 +} + +process_opts "$@" + +echo "This script will help you through the process of installing a" +echo "Debian or Ubuntu distribution in a chroot environment. You will" +echo "have to provide your \"sudo\" password when requested." +echo + +# Error handler +trap 'exit 1' INT TERM QUIT HUP +trap 'sudo apt-get clean; tput bel; echo; echo Failed' EXIT + +# Install any missing applications that this script relies on. If these packages +# are already installed, don't force another "apt-get install". That would +# prevent them from being auto-removed, if they ever become eligible for that. +# And as this script only needs the packages once, there is no good reason to +# introduce a hard dependency on things such as dchroot and debootstrap. +dep= +for i in dchroot debootstrap libwww-perl; do + [ -d /usr/share/doc/"$i" ] || dep="$dep $i" +done +[ -n "$dep" ] && sudo apt-get -y install $dep +sudo apt-get -y install schroot + +# Create directory for chroot +sudo mkdir -p /var/lib/chroot + +# Find chroot environments that can be installed with debootstrap +targets="$(cd /usr/share/debootstrap/scripts + ls | grep '^[a-z]*$')" + +# Ask user to pick one of the available targets +echo "The following targets are available to be installed in a chroot:" +j=1; for i in $targets; do + printf '%4d: %s\n' "$j" "$i" + j=$(($j+1)) +done +while :; do + printf "Which target would you like to install: " + read n + [ "$n" -gt 0 -a "$n" -lt "$j" ] >&/dev/null && break +done +j=1; for i in $targets; do + [ "$j" -eq "$n" ] && { distname="$i"; break; } + j=$(($j+1)) +done +echo + +# On x86-64, ask whether the user wants to install x86-32 or x86-64 +archflag= +arch= +if [ "$(uname -m)" = x86_64 ]; then + while :; do + echo "You are running a 64bit kernel. This allows you to install either a" + printf "32bit or a 64bit chroot environment. %s" \ + "Which one do you want (32, 64) " + read arch + [ "${arch}" == 32 -o "${arch}" == 64 ] && break + done + [ "${arch}" == 32 ] && archflag="--arch i386" || archflag="--arch amd64" + arch="${arch}bit" + echo +fi +target="${distname}${arch}" + +# Don't accidentally overwrite an existing installation +[ -d /var/lib/chroot/"${target}" ] && { + while :; do + echo "This chroot already exists on your machine." + if schroot -l --all-sessions 2>&1 | + sed 's/^session://' | + grep -qs "^${target%bit}-"; then + echo "And it appears to be in active use. Terminate all programs that" + echo "are currently using the chroot environment and then re-run this" + echo "script." + echo "If you still get an error message, you might have stale mounts" + echo "that you forgot to delete. You can always clean up mounts by" + echo "executing \"${target%bit} -c\"." + exit 1 + fi + echo "I can abort installation, I can overwrite the existing chroot," + echo "or I can delete the old one and then exit. What would you like to" + printf "do (a/o/d)? " + read choice + case "${choice}" in + a|A) exit 1;; + o|O) sudo rm -rf "/var/lib/chroot/${target}"; break;; + d|D) sudo rm -rf "/var/lib/chroot/${target}" \ + "/usr/local/bin/${target%bit}" \ + "/etc/schroot/mount-${target}" \ + "/etc/schroot/script-${target}" + sudo sed -ni '/^[[]'"${target%bit}"']$/,${ + :1;n;/^[[]/b2;b1;:2;p;n;b2};p' \ + "/etc/schroot/schroot.conf" + trap '' INT TERM QUIT HUP + trap '' EXIT + echo "Deleted!" + exit 0;; + esac + done + echo +} +sudo mkdir -p /var/lib/chroot/"${target}" + +# Offer to include additional standard repositories for Ubuntu-based chroots. +alt_repos= +grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" && { + while :; do + echo "Would you like to add ${distname}-updates and ${distname}-security " + printf "to the chroot's sources.list (y/n)? " + read alt_repos + case "${alt_repos}" in + y|Y) + alt_repos="y" + break + ;; + n|N) + break + ;; + esac + done + echo +} + +# Check for non-standard file system mount points and ask the user whether +# they should be imported into the chroot environment +# We limit to the first 26 mount points that much some basic heuristics, +# because a) that allows us to enumerate choices with a single character, +# and b) if we find more than 26 mount points, then these are probably +# false-positives and something is very unusual about the system's +# configuration. No need to spam the user with even more information that +# is likely completely irrelevant. +if [ -z "${bind_mounts}" ]; then + mounts="$(awk '$2 != "/" && $2 !~ "^/boot" && $2 !~ "^/home" && + $2 !~ "^/media" && $2 !~ "^/run" && + ($3 ~ "ext[2-4]" || $3 == "reiserfs" || $3 == "btrfs" || + $3 == "xfs" || $3 == "jfs" || $3 == "u?msdos" || + $3 == "v?fat" || $3 == "hfs" || $3 == "ntfs" || + $3 ~ "nfs[4-9]?" || $3 == "smbfs" || $3 == "cifs") { + print $2 + }' /proc/mounts | + head -n26)" + if [ -n "${mounts}" ]; then + echo "You appear to have non-standard mount points that you" + echo "might want to import into the chroot environment:" + echo + sel= + while :; do + # Print a menu, listing all non-default mounts of local or network + # file systems. + j=1; for m in ${mounts}; do + c="$(printf $(printf '\\%03o' $((64+$j))))" + echo "$sel" | grep -qs $c && + state="mounted in chroot" || state="$(tput el)" + printf " $c) %-40s${state}\n" "$m" + j=$(($j+1)) + done + # Allow user to interactively (de-)select any of the entries + echo + printf "Select mount points that you want to be included or press %s" \ + "SPACE to continue" + c="$(getkey | tr a-z A-Z)" + [ "$c" == " " ] && { echo; echo; break; } + if [ -z "$c" ] || + [ "$c" '<' 'A' -o $(ord "$c") -gt $((64 + $(ord "$j"))) ]; then + # Invalid input, ring the console bell + tput bel + else + # Toggle the selection for the given entry + if echo "$sel" | grep -qs $c; then + sel="$(printf "$sel" | sed "s/$c//")" + else + sel="$sel$c" + fi + fi + # Reposition cursor to the top of the list of entries + tput cuu $(($j + 1)) + echo + done + fi + j=1; for m in ${mounts}; do + c="$(chr $(($j + 64)))" + if echo "$sel" | grep -qs $c; then + bind_mounts="${bind_mounts}$m $m none rw,bind 0 0 +" + fi + j=$(($j+1)) + done +fi + +# Remove stale entry from /etc/schroot/schroot.conf. Entries start +# with the target name in square brackets, followed by an arbitrary +# number of lines. The entry stops when either the end of file has +# been reached, or when the beginning of a new target is encountered. +# This means, we cannot easily match for a range of lines in +# "sed". Instead, we actually have to iterate over each line and check +# whether it is the beginning of a new entry. +sudo sed -ni '/^[[]'"${target%bit}"']$/,${:1;n;/^[[]/b2;b1;:2;p;n;b2};p' \ + /etc/schroot/schroot.conf + +# Download base system. This takes some time +if [ -z "${mirror}" ]; then + grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" && + mirror="http://archive.ubuntu.com/ubuntu" || + mirror="http://ftp.us.debian.org/debian" +fi + +sudo ${http_proxy:+http_proxy="${http_proxy}"} debootstrap ${archflag} \ + "${distname}" "/var/lib/chroot/${target}" "$mirror" + +# Add new entry to /etc/schroot/schroot.conf +grep -qs ubuntu.com /usr/share/debootstrap/scripts/"${distname}" && + brand="Ubuntu" || brand="Debian" +if [ -z "${chroot_groups}" ]; then + chroot_groups="${admin},$(id -gn)" +fi +# Older versions of schroot wanted a "priority=" line, whereas recent +# versions deprecate "priority=" and warn if they see it. We don't have +# a good feature test, but scanning for the string "priority=" in the +# existing "schroot.conf" file is a good indication of what to do. +priority=$(grep -qs 'priority=' /etc/schroot/schroot.conf && + echo 'priority=3' || :) +sudo sh -c 'cat >>/etc/schroot/schroot.conf' </etc/schroot/script-'"${target}" +sed '\,^/home[/[:space:]],s/\([,[:space:]]\)bind[[:space:]]/\1rbind /' \ + /etc/schroot/mount-defaults | + sudo sh -c 'cat > /etc/schroot/mount-'"${target}" + +# Add the extra mount points that the user told us about +[ -n "${bind_mounts}" -a "${bind_mounts}" != "NONE" ] && + printf "${bind_mounts}" | + sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" + +# If this system has a "/media" mountpoint, import it into the chroot +# environment. Most modern distributions use this mount point to +# automatically mount devices such as CDROMs, USB sticks, etc... +if [ -d /media ] && + ! grep -qs '^/media' /etc/schroot/mount-"${target}"; then + echo '/media /media none rw,rbind 0 0' | + sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" +fi + +# Share /dev/shm, /run and /run/shm. +grep -qs '^/dev/shm' /etc/schroot/mount-"${target}" || + echo '/dev/shm /dev/shm none rw,bind 0 0' | + sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" +if [ ! -d "/var/lib/chroot/${target}/run" ] && + ! grep -qs '^/run' /etc/schroot/mount-"${target}"; then + echo '/run /run none rw,bind 0 0' | + sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" +fi +if ! grep -qs '^/run/shm' /etc/schroot/mount-"${target}"; then + { [ -d /run ] && echo '/run/shm /run/shm none rw,bind 0 0' || + echo '/dev/shm /run/shm none rw,bind 0 0'; } | + sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" +fi + +# Set up a special directory that changes contents depending on the target +# that is executing. +d="$(readlink -f "${HOME}/chroot" 2>/dev/null || echo "${HOME}/chroot")" +s="${d}/.${target}" +echo "${s} ${d} none rw,bind 0 0" | + sudo sh -c 'cat >>/etc/schroot/mount-'"${target}" +mkdir -p "${s}" + +# Install a helper script to launch commands in the chroot +sudo sh -c 'cat >/usr/local/bin/'"${target%bit}" <<'EOF' +#!/bin/bash + +chroot="${0##*/}" + +wrap() { + # Word-wrap the text passed-in on stdin. Optionally, on continuation lines + # insert the same number of spaces as the number of characters in the + # parameter(s) passed to this function. + # If the "fold" program cannot be found, or if the actual width of the + # terminal cannot be determined, this function doesn't attempt to do any + # wrapping. + local f="$(type -P fold)" + [ -z "${f}" ] && { cat; return; } + local c="$(stty -a /dev/null | + sed 's/.*columns[[:space:]]*\([0-9]*\).*/\1/;t;d')" + [ -z "${c}" ] && { cat; return; } + local i="$(echo "$*"|sed 's/./ /g')" + local j="$(printf %s "${i}"|wc -c)" + if [ "${c}" -gt "${j}" ]; then + dd bs=1 count="${j}" 2>/dev/null + "${f}" -sw "$((${c}-${j}))" | sed '2,$s/^/'"${i}"'/' + else + "${f}" -sw "${c}" + fi +} + +help() { + echo "Usage ${0##*/} [-h|--help] [-c|--clean] [-C|--clean-all] [-l|--list] [--] args" | wrap "Usage ${0##*/} " + echo " help: print this message" | wrap " " + echo " list: list all known chroot environments" | wrap " " + echo " clean: remove all old chroot sessions for \"${chroot}\"" | wrap " " + echo " clean-all: remove all old chroot sessions for all environments" | wrap " " + exit 0 +} + +clean() { + local s t rc + rc=0 + for s in $(schroot -l --all-sessions); do + if [ -n "$1" ]; then + t="${s#session:}" + [ "${t#${chroot}-}" == "${t}" ] && continue + fi + if ls -l /proc/*/{cwd,fd} 2>/dev/null | + fgrep -qs "/var/lib/schroot/mount/${t}"; then + echo "Session \"${t}\" still has active users, not cleaning up" | wrap + rc=1 + continue + fi + sudo schroot -c "${s}" -e || rc=1 + done + exit ${rc} +} + +list() { + for e in $(schroot -l); do + e="${e#chroot:}" + [ -x "/usr/local/bin/${e}" ] || continue + if schroot -l --all-sessions 2>/dev/null | + sed 's/^session://' | + grep -qs "^${e}-"; then + echo "${e} is currently active" + else + echo "${e}" + fi + done + exit 0 +} + +while [ "$#" -ne 0 ]; do + case "$1" in + --) shift; break;; + -h|--help) shift; help;; + -l|--list) shift; list;; + -c|--clean) shift; clean "${chroot}";; + -C|--clean-all) shift; clean;; + *) break;; + esac +done + +# Start a new chroot session and keep track of the session id. We inject this +# id into all processes that run inside the chroot. Unless they go out of their +# way to clear their environment, we can then later identify our child and +# grand-child processes by scanning their environment. +session="$(schroot -c "${chroot}" -b)" +export CHROOT_SESSION_ID="${session}" + +# Set GOMA_TMP_DIR for better handling of goma inside chroot. +export GOMA_TMP_DIR="/tmp/goma_tmp_$CHROOT_SESSION_ID" +mkdir -p "$GOMA_TMP_DIR" + +if [ $# -eq 0 ]; then + # Run an interactive shell session + schroot -c "${session}" -r -p +else + # Run a command inside of the chroot environment + p="$1"; shift + schroot -c "${session}" -r -p "$p" -- "$@" +fi +rc=$? + +# Compute the inode of the root directory inside of the chroot environment. +i=$(schroot -c "${session}" -r -p ls -- -id /proc/self/root/. | + awk '{ print $1 }') 2>/dev/null +other_pids= +while [ -n "$i" ]; do + # Identify processes by the inode number of their root directory. Then + # remove all processes that we know belong to other sessions. We use + # "sort | uniq -u" to do what amounts to a "set substraction operation". + pids=$({ ls -id1 /proc/*/root/. 2>/dev/null | + sed -e 's,^[^0-9]*'$i'.*/\([1-9][0-9]*\)/.*$,\1, + t + d'; + echo "${other_pids}"; + echo "${other_pids}"; } | sort | uniq -u) >/dev/null 2>&1 + # Kill all processes that are still left running in the session. This is + # typically an assortment of daemon processes that were started + # automatically. They result in us being unable to tear down the session + # cleanly. + [ -z "${pids}" ] && break + for j in $pids; do + # Unfortunately, the way that schroot sets up sessions has the + # side-effect of being unable to tell one session apart from another. + # This can result in us attempting to kill processes in other sessions. + # We make a best-effort to avoid doing so. + k="$( ( xargs -0 -n1 /dev/null | + sed 's/^CHROOT_SESSION_ID=/x/;t1;d;:1;q')" + if [ -n "${k}" -a "${k#x}" != "${session}" ]; then + other_pids="${other_pids} +${j}" + continue + fi + kill -9 $pids + done +done +# End the chroot session. This should clean up all temporary files. But if we +# earlier failed to terminate all (daemon) processes inside of the session, +# deleting the session could fail. When that happens, the user has to manually +# clean up the stale files by invoking us with "--clean" after having killed +# all running processes. +schroot -c "${session}" -e +# Since no goma processes are running, we can remove goma directory. +rm -rf "$GOMA_TMP_DIR" +exit $rc +EOF +sudo chown root:root /usr/local/bin/"${target%bit}" +sudo chmod 755 /usr/local/bin/"${target%bit}" + +# Add the standard Ubuntu update repositories if requested. +[ "${alt_repos}" = "y" -a \ + -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && +sudo sed -i '/^deb .* [^ -]\+ main$/p + s/^\(deb .* [^ -]\+\) main/\1-security main/ + p + t1 + d + :1;s/-security main/-updates main/ + t + d' "/var/lib/chroot/${target}/etc/apt/sources.list" + +# Add a few more repositories to the chroot +[ -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && +sudo sed -i 's/ main$/ main restricted universe multiverse/' \ + "/var/lib/chroot/${target}/etc/apt/sources.list" + +# Add the Ubuntu "partner" repository, if available +if [ -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && + HEAD "http://archive.canonical.com/ubuntu/dists/${distname}/partner" \ + >&/dev/null; then + sudo sh -c ' + echo "deb http://archive.canonical.com/ubuntu" \ + "'"${distname}"' partner" \ + >>"/var/lib/chroot/'"${target}"'/etc/apt/sources.list"' +fi + +# Add source repositories, if the user requested we do so +[ "${add_srcs}" = "y" -a \ + -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && +sudo sed -i '/^deb[^-]/p + s/^deb\([^-]\)/deb-src\1/' \ + "/var/lib/chroot/${target}/etc/apt/sources.list" + +# Set apt proxy if host has set http_proxy +if [ -n "${http_proxy}" ]; then + sudo sh -c ' + echo "Acquire::http::proxy \"'"${http_proxy}"'\";" \ + >>"/var/lib/chroot/'"${target}"'/etc/apt/apt.conf"' +fi + +# Update packages +sudo "/usr/local/bin/${target%bit}" /bin/sh -c ' + apt-get update; apt-get -y dist-upgrade' || : + +# Install a couple of missing packages +for i in debian-keyring ubuntu-keyring locales sudo; do + [ -d "/var/lib/chroot/${target}/usr/share/doc/$i" ] || + sudo "/usr/local/bin/${target%bit}" apt-get -y install "$i" || : +done + +# Configure locales +sudo "/usr/local/bin/${target%bit}" /bin/sh -c ' + l='"${LANG:-en_US}"'; l="${l%%.*}" + [ -r /etc/locale.gen ] && + sed -i "s/^# \($l\)/\1/" /etc/locale.gen + locale-gen $LANG en_US en_US.UTF-8' || : + +# Enable multi-arch support, if available +sudo "/usr/local/bin/${target%bit}" dpkg --assert-multi-arch >&/dev/null && + [ -r "/var/lib/chroot/${target}/etc/apt/sources.list" ] && { + sudo sed -i 's/ / [arch=amd64,i386] /' \ + "/var/lib/chroot/${target}/etc/apt/sources.list" + [ -d /var/lib/chroot/${target}/etc/dpkg/dpkg.cfg.d/ ] && + sudo "/usr/local/bin/${target%bit}" dpkg --add-architecture \ + $([ "${arch}" = "32bit" ] && echo amd64 || echo i386) >&/dev/null || + echo foreign-architecture \ + $([ "${arch}" = "32bit" ] && echo amd64 || echo i386) | + sudo sh -c \ + "cat >'/var/lib/chroot/${target}/etc/dpkg/dpkg.cfg.d/multiarch'" +} + +# Configure "sudo" package +sudo "/usr/local/bin/${target%bit}" /bin/sh -c ' + egrep -qs '"'^$(id -nu) '"' /etc/sudoers || + echo '"'$(id -nu) ALL=(ALL) ALL'"' >>/etc/sudoers' + +# Install a few more commonly used packages +sudo "/usr/local/bin/${target%bit}" apt-get -y install \ + autoconf automake1.9 dpkg-dev g++-multilib gcc-multilib gdb less libtool \ + lsof strace + +# If running a 32bit environment on a 64bit machine, install a few binaries +# as 64bit. This is only done automatically if the chroot distro is the same as +# the host, otherwise there might be incompatibilities in build settings or +# runtime dependencies. The user can force it with the '-c' flag. +host_distro=$(grep -s DISTRIB_CODENAME /etc/lsb-release | \ + cut -d "=" -f 2) +if [ "${copy_64}" = "y" -o \ + "${host_distro}" = "${distname}" -a "${arch}" = 32bit ] && \ + file /bin/bash 2>/dev/null | grep -q x86-64; then + readlinepkg=$(sudo "/usr/local/bin/${target%bit}" sh -c \ + 'apt-cache search "lib64readline.\$" | sort | tail -n 1 | cut -d " " -f 1') + sudo "/usr/local/bin/${target%bit}" apt-get -y install \ + lib64expat1 lib64ncurses5 ${readlinepkg} lib64z1 lib64stdc++6 + dep= + for i in binutils gdb; do + [ -d /usr/share/doc/"$i" ] || dep="$dep $i" + done + [ -n "$dep" ] && sudo apt-get -y install $dep + sudo mkdir -p "/var/lib/chroot/${target}/usr/local/lib/amd64" + for i in libbfd libpython; do + lib="$({ ldd /usr/bin/ld; ldd /usr/bin/gdb; } | + grep -s "$i" | awk '{ print $3 }')" + if [ -n "$lib" -a -r "$lib" ]; then + sudo cp "$lib" "/var/lib/chroot/${target}/usr/local/lib/amd64" + fi + done + for lib in libssl libcrypt; do + for path in /usr/lib /usr/lib/x86_64-linux-gnu; do + sudo cp $path/$lib* \ + "/var/lib/chroot/${target}/usr/local/lib/amd64/" >&/dev/null || : + done + done + for i in gdb ld; do + sudo cp /usr/bin/$i "/var/lib/chroot/${target}/usr/local/lib/amd64/" + sudo sh -c "cat >'/var/lib/chroot/${target}/usr/local/bin/$i'" <&/dev/null; then + tmp_script="/tmp/${script##*/}" + cp "${script}" "${tmp_script}" + fi + # Some distributions automatically start an instance of the system- + # wide dbus daemon, cron daemon or of the logging daemon, when + # installing the Chrome build depencies. This prevents the chroot + # session from being closed. So, we always try to shut down any running + # instance of dbus and rsyslog. + sudo /usr/local/bin/"${target%bit}" sh -c "${script} --no-lib32; + rc=$?; + /etc/init.d/cron stop >/dev/null 2>&1 || :; + /etc/init.d/rsyslog stop >/dev/null 2>&1 || :; + /etc/init.d/dbus stop >/dev/null 2>&1 || :; + exit $rc" + rc=$? + [ -n "${tmp_script}" ] && rm -f "${tmp_script}" + [ $rc -ne 0 ] && exit $rc + break + ;; + n|N) + break + ;; + esac + done + echo +fi + +# Check whether ~/chroot is on a (slow) network file system and offer to +# relocate it. Also offer relocation, if the user appears to have multiple +# spindles (as indicated by "${bind_mount}" being non-empty). +# We only offer this option, if it doesn't look as if a chroot environment +# is currently active. Otherwise, relocation is unlikely to work and it +# can be difficult for the user to recover from the failed attempt to relocate +# the ~/chroot directory. +# We don't aim to solve this problem for every configuration, +# but try to help with the common cases. For more advanced configuration +# options, the user can always manually adjust things. +mkdir -p "${HOME}/chroot/" +if [ ! -h "${HOME}/chroot" ] && + ! egrep -qs '^[^[:space:]]*/chroot' /etc/fstab && + { [ -n "${bind_mounts}" -a "${bind_mounts}" != "NONE" ] || + is_network_drive "${HOME}/chroot"; } && + ! egrep -qs '/var/lib/[^/]*chroot/.*/chroot' /proc/mounts; then + echo "${HOME}/chroot is currently located on the same device as your" + echo "home directory." + echo "This might not be what you want. Do you want me to move it somewhere" + echo "else?" + # If the computer has multiple spindles, many users configure all or part of + # the secondary hard disk to be writable by the primary user of this machine. + # Make some reasonable effort to detect this type of configuration and + # then offer a good location for where to put the ~/chroot directory. + suggest= + for i in $(echo "${bind_mounts}"|cut -d ' ' -f 1); do + if [ -d "$i" -a -w "$i" -a \( ! -a "$i/chroot" -o -w "$i/chroot/." \) ] && + ! is_network_drive "$i"; then + suggest="$i" + else + for j in "$i/"*; do + if [ -d "$j" -a -w "$j" -a \ + \( ! -a "$j/chroot" -o -w "$j/chroot/." \) ] && + ! is_network_drive "$j"; then + suggest="$j" + else + for k in "$j/"*; do + if [ -d "$k" -a -w "$k" -a \ + \( ! -a "$k/chroot" -o -w "$k/chroot/." \) ] && + ! is_network_drive "$k"; then + suggest="$k" + break + fi + done + fi + [ -n "${suggest}" ] && break + done + fi + [ -n "${suggest}" ] && break + done + def_suggest="${HOME}" + if [ -n "${suggest}" ]; then + # For home directories that reside on network drives, make our suggestion + # the default option. For home directories that reside on a local drive, + # require that the user manually enters the new location. + if is_network_drive "${HOME}"; then + def_suggest="${suggest}" + else + echo "A good location would probably be in \"${suggest}\"" + fi + fi + while :; do + printf "Physical location [${def_suggest}]: " + read dir + [ -z "${dir}" ] && dir="${def_suggest}" + [ "${dir%%/}" == "${HOME%%/}" ] && break + if ! [ -d "${dir}" -a -w "${dir}" ] || + [ -a "${dir}/chroot" -a ! -w "${dir}/chroot/." ]; then + echo "Cannot write to ${dir}/chroot. Please try again" + else + mv "${HOME}/chroot" "${dir}/chroot" + ln -s "${dir}/chroot" "${HOME}/chroot" + for i in $(list_all_chroots); do + sudo "$i" mkdir -p "${dir}/chroot" + done + sudo sed -i "s,${HOME}/chroot,${dir}/chroot,g" /etc/schroot/mount-* + break + fi + done +fi + +# Clean up package files +sudo schroot -c "${target%bit}" -p -- apt-get clean +sudo apt-get clean + +trap '' INT TERM QUIT HUP +trap '' EXIT + +# Let the user know what we did +cat < 0) + + add_to_path = []; + first_entry = argv[0]; + if first_entry.startswith('ADD_TO_PATH='): + argv = argv[1:]; + add_to_path = first_entry.replace('ADD_TO_PATH=', '', 1).split(':') + + # Still need something to run. + assert(len(argv) > 0) + + clean_env = {} + + # Pull over the whitelisted keys. + for key in env_key_whitelist: + val = os.environ.get(key, None) + if not val is None: + clean_env[key] = val + + # Collect the developer dir as set via Xcode, defaulting it. + dev_prefix = os.environ.get('DEVELOPER_DIR', '/Developer/') + if dev_prefix[-1:] != '/': + dev_prefix += '/' + + # Now pull in PATH, but remove anything Xcode might have added. + initial_path = os.environ.get('PATH', '') + filtered_chunks = \ + [x for x in initial_path.split(':') if not x.startswith(dev_prefix)] + if filtered_chunks: + clean_env['PATH'] = ':'.join(add_to_path + filtered_chunks) + + # Add any KEY=VALUE args before the command to the cleaned environment. + args = argv[:] + while '=' in args[0]: + (key, val) = args[0].split('=', 1) + clean_env[key] = val + args = args[1:] + + # Still need something to run. + assert(len(args) > 0) + + # Off it goes... + os.execvpe(args[0], args, clean_env) + # Should never get here, so return a distinctive, non-zero status code. + return 66 + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/engine/src/flutter/build/ios/grit_whitelist.txt b/engine/src/flutter/build/ios/grit_whitelist.txt new file mode 100644 index 0000000000..8fb4f0ec90 --- /dev/null +++ b/engine/src/flutter/build/ios/grit_whitelist.txt @@ -0,0 +1,1158 @@ +IDR_ABOUT_DOM_DISTILLER_CSS +IDR_ABOUT_DOM_DISTILLER_HTML +IDR_ABOUT_DOM_DISTILLER_JS +IDR_ABOUT_STATS_HTML +IDR_ABOUT_STATS_JS +IDR_ABOUT_VERSION_CSS +IDR_ABOUT_VERSION_HTML +IDR_ABOUT_VERSION_JS +IDR_CONTEXTUAL_SEARCH_PROMO_HTML +IDR_CONTROLLED_SETTING_MANDATORY +IDR_CRASHES_HTML +IDR_CRASHES_JS +IDR_CREDITS_HTML +IDR_CREDITS_JS +IDR_CREDIT_CARD_CVC_HINT +IDR_CREDIT_CARD_CVC_HINT_AMEX +IDR_DATA_REDUCTION_PROXY_INTERSTITIAL_HTML +IDR_DEFAULT_FAVICON +IDR_DEFAULT_FAVICON_32 +IDR_DEFAULT_FAVICON_64 +IDR_DIR_HEADER_HTML +IDR_DISTILLABLE_PAGE_SERIALIZED_MODEL +IDR_DISTILLER_CSS +IDR_DISTILLER_JS +IDR_DOM_DISTILLER_VIEWER_HTML +IDR_DOM_DISTILLER_VIEWER_JS +IDR_EXTRACT_PAGE_FEATURES_JS +IDR_FLAGS_FAVICON +IDR_FLAGS_HTML +IDR_FLAGS_JS +IDR_GCM_INTERNALS_CSS +IDR_GCM_INTERNALS_HTML +IDR_GCM_INTERNALS_JS +IDR_HISTORY_FAVICON +IDR_HISTORY_HTML +IDR_HISTORY_JS +IDR_INCOGNITO_TAB_HTML +IDR_INFOBAR_AUTOFILL_CC +IDR_INFOBAR_AUTOLOGIN +IDR_INFOBAR_RESTORE_SESSION +IDR_INFOBAR_SAVE_PASSWORD +IDR_INFOBAR_TRANSLATE_IOS +IDR_INFOBAR_WARNING +IDR_IS_DISTILLABLE_JS +IDR_LOCATION_BAR_HTTP +IDR_NET_ERROR_HTML +IDR_NET_EXPORT_HTML +IDR_NET_EXPORT_JS +IDR_NET_INTERNALS_INDEX_HTML +IDR_NET_INTERNALS_INDEX_JS +IDR_OMAHA_HTML +IDR_OMAHA_JS +IDR_OMNIBOX_CALCULATOR +IDR_OMNIBOX_CLEAR_IOS +IDR_OMNIBOX_CLEAR_OTR_IOS +IDR_OMNIBOX_CLEAR_OTR_PRESSED_IOS +IDR_OMNIBOX_CLEAR_PRESSED_IOS +IDR_OMNIBOX_EXTENSION_APP +IDR_OMNIBOX_HISTORY +IDR_OMNIBOX_HISTORY_INCOGNITO +IDR_OMNIBOX_HTTP +IDR_OMNIBOX_HTTPS_INVALID +IDR_OMNIBOX_HTTPS_POLICY_WARNING +IDR_OMNIBOX_HTTPS_VALID +IDR_OMNIBOX_HTTPS_WARNING +IDR_OMNIBOX_HTTP_INCOGNITO +IDR_OMNIBOX_KEYBOARD_VIEW_APPEND +IDR_OMNIBOX_KEYBOARD_VIEW_APPEND_HIGHLIGHTED +IDR_OMNIBOX_KEYBOARD_VIEW_APPEND_INCOGNITO +IDR_OMNIBOX_KEYBOARD_VIEW_APPEND_INCOGNITO_HIGHLIGHTED +IDR_OMNIBOX_SEARCH +IDR_OMNIBOX_SEARCH_INCOGNITO +IDR_OMNIBOX_SEARCH_SECURED +IDR_OMNIBOX_STAR +IDR_OMNIBOX_STAR_INCOGNITO +IDR_OTHER_DEVICES_JS +IDR_PAGEINFO_BAD +IDR_PAGEINFO_GOOD +IDR_PAGEINFO_INFO +IDR_PAGEINFO_WARNING_MAJOR +IDR_PAGEINFO_WARNING_MINOR +IDR_POLICY_CSS +IDR_POLICY_HTML +IDR_POLICY_JS +IDR_PRINTER_FAVICON +IDR_SAD_FAVICON +IDR_SAD_TAB +IDR_SECURITY_INTERSTITIAL_HTML +IDR_SIGNIN_INTERNALS_INDEX_HTML +IDR_SIGNIN_INTERNALS_INDEX_JS +IDR_SYNC_INTERNALS_ABOUT_JS +IDR_SYNC_INTERNALS_CHROME_SYNC_JS +IDR_SYNC_INTERNALS_DATA_JS +IDR_SYNC_INTERNALS_EVENTS_JS +IDR_SYNC_INTERNALS_INDEX_HTML +IDR_SYNC_INTERNALS_INDEX_JS +IDR_SYNC_INTERNALS_SEARCH_JS +IDR_SYNC_INTERNALS_SYNC_LOG_JS +IDR_SYNC_INTERNALS_SYNC_NODE_BROWSER_JS +IDR_SYNC_INTERNALS_SYNC_SEARCH_JS +IDR_SYNC_INTERNALS_TYPES_JS +IDR_TOOLBAR_SHADOW_FULL_BLEED +IDR_TRANSLATE_JS +IDR_UBER_UTILS_JS +IDR_WEBUI_CSS_TEXT_DEFAULTS +IDR_WEBUI_I18N_TEMPLATE_JS +IDR_WEBUI_I18N_TEMPLATE_POLYMER_JS +IDR_WEBUI_JSTEMPLATE_JS +IDR_WEBUI_JS_LOAD_TIME_DATA +IDS_ABOUT_MAC +IDS_ABOUT_VERSION_COMMAND_LINE +IDS_ABOUT_VERSION_COMPANY_NAME +IDS_ABOUT_VERSION_COPYRIGHT +IDS_ABOUT_VERSION_EXECUTABLE_PATH +IDS_ABOUT_VERSION_OFFICIAL +IDS_ABOUT_VERSION_OS +IDS_ABOUT_VERSION_PATH_NOTFOUND +IDS_ABOUT_VERSION_PROFILE_PATH +IDS_ABOUT_VERSION_REVISION +IDS_ABOUT_VERSION_TITLE +IDS_ABOUT_VERSION_UNOFFICIAL +IDS_ABOUT_VERSION_USER_AGENT +IDS_ABOUT_VERSION_VARIATIONS +IDS_ACCEPT_LANGUAGES +IDS_ACCNAME_BACK +IDS_ACCNAME_CLEAR_TEXT +IDS_ACCNAME_FORWARD +IDS_ACCNAME_LOCATION +IDS_ACCNAME_VOICE_SEARCH +IDS_ALLOW_INSECURE_CONTENT_BUTTON +IDS_ALTERNATE_NAV_URL_VIEW_LABEL +IDS_ANNOTATED_SUGGESTION +IDS_APP_CANCEL +IDS_APP_OK +IDS_APP_UNTITLED_SHORTCUT_FILE_NAME +IDS_AUTOCOMPLETE_SEARCH_DESCRIPTION +IDS_AUTOFILL_ADDRESS_LINE_SEPARATOR +IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR +IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_NETWORK +IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_PERMANENT +IDS_AUTOFILL_CARD_UNMASK_PROMPT_ERROR_TRY_AGAIN +IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS +IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_AMEX +IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_EXPIRED +IDS_AUTOFILL_CARD_UNMASK_PROMPT_INSTRUCTIONS_EXPIRED_AMEX +IDS_AUTOFILL_CARD_UNMASK_PROMPT_TITLE +IDS_AUTOFILL_CARD_UNMASK_PROMPT_UPDATE_TITLE +IDS_AUTOFILL_CC_AMEX +IDS_AUTOFILL_CC_AMEX_SHORT +IDS_AUTOFILL_CC_DINERS +IDS_AUTOFILL_CC_DISCOVER +IDS_AUTOFILL_CC_GENERIC +IDS_AUTOFILL_CC_INFOBAR_ACCEPT +IDS_AUTOFILL_CC_INFOBAR_DENY +IDS_AUTOFILL_CC_INFOBAR_TEXT +IDS_AUTOFILL_CC_JCB +IDS_AUTOFILL_CC_MASTERCARD +IDS_AUTOFILL_CC_UNION_PAY +IDS_AUTOFILL_CC_VISA +IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM +IDS_AUTOFILL_DELETE_AUTOCOMPLETE_SUGGESTION_CONFIRMATION_BODY +IDS_AUTOFILL_DELETE_CREDIT_CARD_SUGGESTION_CONFIRMATION_BODY +IDS_AUTOFILL_DELETE_PROFILE_SUGGESTION_CONFIRMATION_BODY +IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK +IDS_AUTOFILL_FIELD_LABEL_AREA +IDS_AUTOFILL_FIELD_LABEL_COUNTY +IDS_AUTOFILL_FIELD_LABEL_DEPARTMENT +IDS_AUTOFILL_FIELD_LABEL_DISTRICT +IDS_AUTOFILL_FIELD_LABEL_EMIRATE +IDS_AUTOFILL_FIELD_LABEL_ISLAND +IDS_AUTOFILL_FIELD_LABEL_PARISH +IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE +IDS_AUTOFILL_FIELD_LABEL_PREFECTURE +IDS_AUTOFILL_FIELD_LABEL_PROVINCE +IDS_AUTOFILL_FIELD_LABEL_STATE +IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE +IDS_AUTOFILL_OPTIONS_POPUP +IDS_AUTOFILL_PASSWORD_FIELD_SUGGESTIONS_TITLE +IDS_AUTOFILL_SCAN_CREDIT_CARD +IDS_AUTOFILL_WARNING_FORM_DISABLED +IDS_AUTOFILL_WARNING_INSECURE_CONNECTION +IDS_AUTOLOGIN_INFOBAR_CANCEL_BUTTON +IDS_AUTOLOGIN_INFOBAR_MESSAGE +IDS_AUTOLOGIN_INFOBAR_OK_BUTTON +IDS_BLOCKED_DISPLAYING_INSECURE_CONTENT +IDS_BLOCK_INSECURE_CONTENT_BUTTON +IDS_BOOKMARK_ADD_EDITOR_TITLE +IDS_BOOKMARK_ALL_TABS_DIALOG_TITLE +IDS_BOOKMARK_BAR_FOLDER_NAME +IDS_BOOKMARK_BAR_MANAGED_FOLDER_DEFAULT_NAME +IDS_BOOKMARK_BAR_MANAGED_FOLDER_DOMAIN_NAME +IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME +IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME +IDS_BOOKMARK_BAR_REDO +IDS_BOOKMARK_BAR_REDO_ADD +IDS_BOOKMARK_BAR_REDO_DELETE +IDS_BOOKMARK_BAR_REDO_EDIT +IDS_BOOKMARK_BAR_REDO_MOVE +IDS_BOOKMARK_BAR_REDO_REORDER +IDS_BOOKMARK_BAR_SUPERVISED_FOLDER_DEFAULT_NAME +IDS_BOOKMARK_BAR_UNDO +IDS_BOOKMARK_BAR_UNDO_ADD +IDS_BOOKMARK_BAR_UNDO_DELETE +IDS_BOOKMARK_BAR_UNDO_EDIT +IDS_BOOKMARK_BAR_UNDO_MOVE +IDS_BOOKMARK_BAR_UNDO_REORDER +IDS_BOOKMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER +IDS_BOOKMARK_BUBBLE_REMOVE_BOOKMARK +IDS_BOOKMARK_EDITOR_CONFIRM_DELETE +IDS_BOOKMARK_EDITOR_NEW_FOLDER_NAME +IDS_BOOKMARK_EDITOR_TITLE +IDS_BOOKMARK_FOLDER_CHOOSER_TITLE +IDS_BOOKMARK_FOLDER_EDITOR_TITLE +IDS_BOOKMARK_FOLDER_EDITOR_WINDOW_TITLE +IDS_BOOKMARK_FOLDER_EDITOR_WINDOW_TITLE_NEW +IDS_BOOKMARK_MANAGER_FOLDER_SECTION +IDS_BOOKMARK_MANAGER_FOLDER_TITLE +IDS_BOOKMARK_MANAGER_NAME_INPUT_PLACE_HOLDER +IDS_BOOKMARK_MANAGER_REMOVE_TITLE +IDS_BOOKMARK_MANAGER_URL_INPUT_PLACE_HOLDER +IDS_BOOKMARK_NEW_FOLDER_BUTTON_TITLE +IDS_CANCEL +IDS_CERT_ERROR_AUTHORITY_INVALID_DESCRIPTION +IDS_CERT_ERROR_AUTHORITY_INVALID_DETAILS +IDS_CERT_ERROR_AUTHORITY_INVALID_EXTRA_INFO_2 +IDS_CERT_ERROR_AUTHORITY_INVALID_TITLE +IDS_CERT_ERROR_CHAIN_EXPIRED_DESCRIPTION +IDS_CERT_ERROR_CHAIN_EXPIRED_DETAILS +IDS_CERT_ERROR_COMMON_NAME_INVALID_DESCRIPTION +IDS_CERT_ERROR_COMMON_NAME_INVALID_DETAILS +IDS_CERT_ERROR_COMMON_NAME_INVALID_EXTRA_INFO_2 +IDS_CERT_ERROR_COMMON_NAME_INVALID_TITLE +IDS_CERT_ERROR_CONTAINS_ERRORS_DESCRIPTION +IDS_CERT_ERROR_CONTAINS_ERRORS_DETAILS +IDS_CERT_ERROR_CONTAINS_ERRORS_EXTRA_INFO_2 +IDS_CERT_ERROR_CONTAINS_ERRORS_TITLE +IDS_CERT_ERROR_EXPIRED_DESCRIPTION +IDS_CERT_ERROR_EXPIRED_DETAILS +IDS_CERT_ERROR_EXPIRED_DETAILS_EXTRA_INFO_2 +IDS_CERT_ERROR_EXPIRED_TITLE +IDS_CERT_ERROR_EXTRA_INFO_1 +IDS_CERT_ERROR_EXTRA_INFO_TITLE +IDS_CERT_ERROR_INVALID_CERT_DESCRIPTION +IDS_CERT_ERROR_INVALID_CERT_DETAILS +IDS_CERT_ERROR_INVALID_CERT_EXTRA_INFO_2 +IDS_CERT_ERROR_INVALID_CERT_TITLE +IDS_CERT_ERROR_NAME_CONSTRAINT_VIOLATION_DESCRIPTION +IDS_CERT_ERROR_NAME_CONSTRAINT_VIOLATION_DETAILS +IDS_CERT_ERROR_NAME_CONSTRAINT_VIOLATION_TITLE +IDS_CERT_ERROR_NOT_YET_VALID_DESCRIPTION +IDS_CERT_ERROR_NOT_YET_VALID_DETAILS +IDS_CERT_ERROR_NOT_YET_VALID_DETAILS_EXTRA_INFO_2 +IDS_CERT_ERROR_NOT_YET_VALID_TITLE +IDS_CERT_ERROR_NO_REVOCATION_MECHANISM_DESCRIPTION +IDS_CERT_ERROR_NO_REVOCATION_MECHANISM_DETAILS +IDS_CERT_ERROR_NO_REVOCATION_MECHANISM_TITLE +IDS_CERT_ERROR_REVOKED_CERT_DESCRIPTION +IDS_CERT_ERROR_REVOKED_CERT_DETAILS +IDS_CERT_ERROR_REVOKED_CERT_EXTRA_INFO_2 +IDS_CERT_ERROR_REVOKED_CERT_TITLE +IDS_CERT_ERROR_UNABLE_TO_CHECK_REVOCATION_DESCRIPTION +IDS_CERT_ERROR_UNABLE_TO_CHECK_REVOCATION_DETAILS +IDS_CERT_ERROR_UNABLE_TO_CHECK_REVOCATION_TITLE +IDS_CERT_ERROR_UNKNOWN_ERROR_DESCRIPTION +IDS_CERT_ERROR_UNKNOWN_ERROR_DETAILS +IDS_CERT_ERROR_UNKNOWN_ERROR_TITLE +IDS_CERT_ERROR_WEAK_KEY_DESCRIPTION +IDS_CERT_ERROR_WEAK_KEY_DETAILS +IDS_CERT_ERROR_WEAK_KEY_EXTRA_INFO_2 +IDS_CERT_ERROR_WEAK_KEY_TITLE +IDS_CERT_ERROR_WEAK_SIGNATURE_ALGORITHM_DESCRIPTION +IDS_CERT_ERROR_WEAK_SIGNATURE_ALGORITHM_DETAILS +IDS_CERT_ERROR_WEAK_SIGNATURE_ALGORITHM_EXTRA_INFO_2 +IDS_CERT_ERROR_WEAK_SIGNATURE_ALGORITHM_TITLE +IDS_CHROME_TO_DEVICE_PRINT_TO_PHONE +IDS_CHROME_TO_DEVICE_SNAPSHOTS +IDS_CLOSE +IDS_CONTEXTUAL_SEARCH_HEADER +IDS_CONTEXTUAL_SEARCH_PROMO_DESCRIPTION_1 +IDS_CONTEXTUAL_SEARCH_PROMO_DESCRIPTION_2 +IDS_CONTEXTUAL_SEARCH_PROMO_FEATURE_NAME +IDS_CONTEXTUAL_SEARCH_PROMO_OPTIN +IDS_CONTEXTUAL_SEARCH_PROMO_OPTOUT +IDS_COULDNT_OPEN_PROFILE_ERROR +IDS_CRASHES_BUG_LINK_LABEL +IDS_CRASHES_CRASH_COUNT_BANNER_FORMAT +IDS_CRASHES_CRASH_HEADER_FORMAT +IDS_CRASHES_CRASH_TIME_FORMAT +IDS_CRASHES_DISABLED_HEADER +IDS_CRASHES_DISABLED_MESSAGE +IDS_CRASHES_NO_CRASHES_MESSAGE +IDS_CRASHES_TITLE +IDS_CRASHES_UPLOAD_MESSAGE +IDS_DATA_REDUCTION_PROXY_BACK_BUTTON +IDS_DATA_REDUCTION_PROXY_CANNOT_PROXY_HEADING +IDS_DATA_REDUCTION_PROXY_CANNOT_PROXY_PRIMARY_PARAGRAPH +IDS_DATA_REDUCTION_PROXY_CANNOT_PROXY_SECONDARY_PARAGRAPH +IDS_DATA_REDUCTION_PROXY_CONTINUE_BUTTON +IDS_DATA_REDUCTION_PROXY_TITLE +IDS_DEFAULT_AVATAR_NAME_10 +IDS_DEFAULT_AVATAR_NAME_11 +IDS_DEFAULT_AVATAR_NAME_12 +IDS_DEFAULT_AVATAR_NAME_13 +IDS_DEFAULT_AVATAR_NAME_14 +IDS_DEFAULT_AVATAR_NAME_15 +IDS_DEFAULT_AVATAR_NAME_16 +IDS_DEFAULT_AVATAR_NAME_17 +IDS_DEFAULT_AVATAR_NAME_18 +IDS_DEFAULT_AVATAR_NAME_19 +IDS_DEFAULT_AVATAR_NAME_20 +IDS_DEFAULT_AVATAR_NAME_21 +IDS_DEFAULT_AVATAR_NAME_22 +IDS_DEFAULT_AVATAR_NAME_23 +IDS_DEFAULT_AVATAR_NAME_24 +IDS_DEFAULT_AVATAR_NAME_25 +IDS_DEFAULT_AVATAR_NAME_26 +IDS_DEFAULT_AVATAR_NAME_8 +IDS_DEFAULT_AVATAR_NAME_9 +IDS_DEFAULT_ENCODING +IDS_DEFAULT_PROFILE_NAME +IDS_DEFAULT_TAB_TITLE +IDS_DELETE +IDS_DISABLE_TOUCH_ADJUSTMENT_DESCRIPTION +IDS_DISABLE_TOUCH_ADJUSTMENT_NAME +IDS_DOM_DISTILLER_JAVASCRIPT_DISABLED_CONTENT +IDS_DOM_DISTILLER_QUALITY_ANSWER_NO +IDS_DOM_DISTILLER_QUALITY_ANSWER_YES +IDS_DOM_DISTILLER_QUALITY_QUESTION +IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_CONTENT +IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_TITLE +IDS_DOM_DISTILLER_VIEWER_LOADING_STRING +IDS_DOM_DISTILLER_VIEWER_LOADING_TITLE +IDS_DOM_DISTILLER_VIEWER_NO_DATA_CONTENT +IDS_DOM_DISTILLER_VIEWER_NO_DATA_TITLE +IDS_DOM_DISTILLER_VIEWER_VIEW_ORIGINAL +IDS_DOM_DISTILLER_WEBUI_ENTRY_ADD +IDS_DOM_DISTILLER_WEBUI_ENTRY_ADD_FAILED +IDS_DOM_DISTILLER_WEBUI_ENTRY_URL +IDS_DOM_DISTILLER_WEBUI_FETCHING_ENTRIES +IDS_DOM_DISTILLER_WEBUI_REFRESH +IDS_DOM_DISTILLER_WEBUI_TITLE +IDS_DOM_DISTILLER_WEBUI_VIEW_URL +IDS_DOM_DISTILLER_WEBUI_VIEW_URL_FAILED +IDS_DONE +IDS_EASY_UNLOCK_SCREENLOCK_USER_POD_AUTH_VALUE +IDS_EDIT_FIND_MAC +IDS_EMPTY_KEYWORD_VALUE +IDS_ERRORPAGES_BUTTON_LESS +IDS_ERRORPAGES_BUTTON_MORE +IDS_ERRORPAGES_BUTTON_RELOAD +IDS_ERRORPAGES_BUTTON_SHOW_SAVED_COPY +IDS_ERRORPAGES_BUTTON_SHOW_SAVED_COPY_HELP +IDS_ERRORPAGES_DETAILS_ADDRESS_UNREACHABLE +IDS_ERRORPAGES_DETAILS_BAD_GATEWAY +IDS_ERRORPAGES_DETAILS_BAD_SSL_CLIENT_AUTH_CERT +IDS_ERRORPAGES_DETAILS_BLOCKED +IDS_ERRORPAGES_DETAILS_BLOCKED_BY_ADMINISTRATOR +IDS_ERRORPAGES_DETAILS_BLOCKED_ENROLLMENT_CHECK_PENDING +IDS_ERRORPAGES_DETAILS_CACHE_MISS +IDS_ERRORPAGES_DETAILS_CACHE_READ_FAILURE +IDS_ERRORPAGES_DETAILS_CONNECTION_CLOSED +IDS_ERRORPAGES_DETAILS_CONNECTION_FAILED +IDS_ERRORPAGES_DETAILS_CONNECTION_REFUSED +IDS_ERRORPAGES_DETAILS_CONNECTION_RESET +IDS_ERRORPAGES_DETAILS_DNS_PROBE_RUNNING +IDS_ERRORPAGES_DETAILS_DOWNLOAD_FILE_TYPE_ERROR +IDS_ERRORPAGES_DETAILS_EMPTY_RESPONSE +IDS_ERRORPAGES_DETAILS_FILE_ACCESS_DENIED +IDS_ERRORPAGES_DETAILS_FILE_NOT_FOUND +IDS_ERRORPAGES_DETAILS_FORBIDDEN +IDS_ERRORPAGES_DETAILS_GATEWAY_TIMEOUT +IDS_ERRORPAGES_DETAILS_GONE +IDS_ERRORPAGES_DETAILS_HTTP_VERSION_NOT_SUPPORTED +IDS_ERRORPAGES_DETAILS_ICANN_NAME_COLLISION +IDS_ERRORPAGES_DETAILS_INTERNAL_SERVER_ERROR +IDS_ERRORPAGES_DETAILS_INTERNET_DISCONNECTED +IDS_ERRORPAGES_DETAILS_NAME_NOT_RESOLVED +IDS_ERRORPAGES_DETAILS_NETWORK_ACCESS_DENIED +IDS_ERRORPAGES_DETAILS_NETWORK_CHANGED +IDS_ERRORPAGES_DETAILS_NETWORK_IO_SUSPENDED +IDS_ERRORPAGES_DETAILS_NOT_IMPLEMENTED +IDS_ERRORPAGES_DETAILS_PINNING_FAILURE +IDS_ERRORPAGES_DETAILS_PROXY_CONNECTION_FAILED +IDS_ERRORPAGES_DETAILS_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION +IDS_ERRORPAGES_DETAILS_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH +IDS_ERRORPAGES_DETAILS_RESPONSE_HEADERS_MULTIPLE_LOCATION +IDS_ERRORPAGES_DETAILS_SERVICE_UNAVAILABLE +IDS_ERRORPAGES_DETAILS_SSL_FALLBACK_BEYOND_MINIMUM_VERSION +IDS_ERRORPAGES_DETAILS_SSL_PROTOCOL_ERROR +IDS_ERRORPAGES_DETAILS_SSL_VERSION_OR_CIPHER_MISMATCH +IDS_ERRORPAGES_DETAILS_TEMPORARILY_THROTTLED +IDS_ERRORPAGES_DETAILS_TIMED_OUT +IDS_ERRORPAGES_DETAILS_TOO_MANY_REDIRECTS +IDS_ERRORPAGES_DETAILS_UNKNOWN +IDS_ERRORPAGES_ERROR_CODE +IDS_ERRORPAGES_HEADING_ACCESS_DENIED +IDS_ERRORPAGES_HEADING_BAD_SSL_CLIENT_AUTH_CERT +IDS_ERRORPAGES_HEADING_BLOCKED +IDS_ERRORPAGES_HEADING_BLOCKED_BY_ADMINISTRATOR +IDS_ERRORPAGES_HEADING_CACHE_MISS +IDS_ERRORPAGES_HEADING_CACHE_READ_FAILURE +IDS_ERRORPAGES_HEADING_DOWNLOAD_FILE_TYPE_ERROR +IDS_ERRORPAGES_HEADING_DUPLICATE_HEADERS +IDS_ERRORPAGES_HEADING_EMPTY_RESPONSE +IDS_ERRORPAGES_HEADING_FILE_ACCESS_DENIED +IDS_ERRORPAGES_HEADING_HTTP_SERVER_ERROR +IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED +IDS_ERRORPAGES_HEADING_NETWORK_ACCESS_DENIED +IDS_ERRORPAGES_HEADING_NETWORK_IO_SUSPENDED +IDS_ERRORPAGES_HEADING_NOT_AVAILABLE +IDS_ERRORPAGES_HEADING_NOT_FOUND +IDS_ERRORPAGES_HEADING_PINNING_FAILURE +IDS_ERRORPAGES_HEADING_PROXY_CONNECTION_FAILED +IDS_ERRORPAGES_HEADING_SSL_FALLBACK_BEYOND_MINIMUM_VERSION +IDS_ERRORPAGES_HEADING_SSL_PROTOCOL_ERROR +IDS_ERRORPAGES_HEADING_SSL_VERSION_OR_CIPHER_MISMATCH +IDS_ERRORPAGES_HEADING_TOO_MANY_REDIRECTS +IDS_ERRORPAGES_HEADING_WEAK_SERVER_EPHEMERAL_DH_KEY +IDS_ERRORPAGES_HTTP_POST_WARNING +IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_BODY +IDS_ERRORPAGES_SUGGESTION_CHECK_CONNECTION_HEADER +IDS_ERRORPAGES_SUGGESTION_CONTACT_ADMINISTRATOR +IDS_ERRORPAGES_SUGGESTION_DNS_CONFIG +IDS_ERRORPAGES_SUGGESTION_FIREWALL_CONFIG +IDS_ERRORPAGES_SUGGESTION_GOOGLE_SEARCH +IDS_ERRORPAGES_SUGGESTION_LEARNMORE_BODY +IDS_ERRORPAGES_SUGGESTION_NETWORK_PREDICTION +IDS_ERRORPAGES_SUGGESTION_PROXY_CONFIG +IDS_ERRORPAGES_SUGGESTION_PROXY_DISABLE_PLATFORM +IDS_ERRORPAGES_SUGGESTION_RELOAD +IDS_ERRORPAGES_SUGGESTION_RELOAD_REPOST_BODY +IDS_ERRORPAGES_SUGGESTION_RELOAD_REPOST_HEADER +IDS_ERRORPAGES_SUGGESTION_VIEW_POLICIES +IDS_ERRORPAGES_SUMMARY_ADDRESS_UNREACHABLE +IDS_ERRORPAGES_SUMMARY_BAD_GATEWAY +IDS_ERRORPAGES_SUMMARY_BAD_SSL_CLIENT_AUTH_CERT +IDS_ERRORPAGES_SUMMARY_BLOCKED +IDS_ERRORPAGES_SUMMARY_BLOCKED_BY_ADMINISTRATOR +IDS_ERRORPAGES_SUMMARY_BLOCKED_ENROLLMENT_CHECK_PENDING +IDS_ERRORPAGES_SUMMARY_CACHE_MISS +IDS_ERRORPAGES_SUMMARY_CACHE_READ_FAILURE +IDS_ERRORPAGES_SUMMARY_CONNECTION_REFUSED +IDS_ERRORPAGES_SUMMARY_CONNECTION_RESET +IDS_ERRORPAGES_SUMMARY_DNS_PROBE_RUNNING +IDS_ERRORPAGES_SUMMARY_DOWNLOAD_FILE_TYPE_ERROR +IDS_ERRORPAGES_SUMMARY_DUPLICATE_HEADERS +IDS_ERRORPAGES_SUMMARY_EMPTY_RESPONSE +IDS_ERRORPAGES_SUMMARY_FILE_ACCESS_DENIED +IDS_ERRORPAGES_SUMMARY_FORBIDDEN +IDS_ERRORPAGES_SUMMARY_GATEWAY_TIMEOUT +IDS_ERRORPAGES_SUMMARY_GONE +IDS_ERRORPAGES_SUMMARY_ICANN_NAME_COLLISION +IDS_ERRORPAGES_SUMMARY_INTERNAL_SERVER_ERROR +IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED +IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_INSTRUCTIONS_TEMPLATE +IDS_ERRORPAGES_SUMMARY_INTERNET_DISCONNECTED_PLATFORM +IDS_ERRORPAGES_SUMMARY_NAME_NOT_RESOLVED +IDS_ERRORPAGES_SUMMARY_NETWORK_ACCESS_DENIED +IDS_ERRORPAGES_SUMMARY_NETWORK_CHANGED +IDS_ERRORPAGES_SUMMARY_NETWORK_IO_SUSPENDED +IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE +IDS_ERRORPAGES_SUMMARY_NOT_FOUND +IDS_ERRORPAGES_SUMMARY_PINNING_FAILURE +IDS_ERRORPAGES_SUMMARY_PROXY_CONNECTION_FAILED +IDS_ERRORPAGES_SUMMARY_SERVICE_UNAVAILABLE +IDS_ERRORPAGES_SUMMARY_SSL_FALLBACK_BEYOND_MINIMUM_VERSION +IDS_ERRORPAGES_SUMMARY_SSL_PROTOCOL_ERROR +IDS_ERRORPAGES_SUMMARY_SSL_VERSION_OR_CIPHER_MISMATCH +IDS_ERRORPAGES_SUMMARY_TEMPORARILY_THROTTLED +IDS_ERRORPAGES_SUMMARY_TIMED_OUT +IDS_ERRORPAGES_SUMMARY_TOO_MANY_REDIRECTS +IDS_ERRORPAGES_SUMMARY_WEAK_SERVER_EPHEMERAL_DH_KEY +IDS_ERRORPAGES_SUMMARY_WEBSITE_CANNOT_HANDLE +IDS_ERRORPAGES_TITLE_ACCESS_DENIED +IDS_ERRORPAGES_TITLE_BLOCKED +IDS_ERRORPAGES_TITLE_LOAD_FAILED +IDS_ERRORPAGES_TITLE_NOT_AVAILABLE +IDS_ERRORPAGES_TITLE_NOT_FOUND +IDS_ERRORPAGE_NET_BUTTON_DETAILS +IDS_ERRORPAGE_NET_BUTTON_HIDE_DETAILS +IDS_EXTENSION_KEYWORD_COMMAND +IDS_FEEDBACK_REPORT_PAGE_TITLE +IDS_FEEDBACK_REPORT_URL_LABEL +IDS_FEEDBACK_SEND_REPORT +IDS_FEEDBACK_USER_EMAIL_LABEL +IDS_FIND_IN_PAGE_CLOSE_TOOLTIP +IDS_FIND_IN_PAGE_COUNT +IDS_FIND_IN_PAGE_NEXT_TOOLTIP +IDS_FIND_IN_PAGE_PREVIOUS_TOOLTIP +IDS_FLAGS_ACCELERATED_FIXED_ROOT_BACKGROUND_DESCRIPTION +IDS_FLAGS_ACCELERATED_FIXED_ROOT_BACKGROUND_NAME +IDS_FLAGS_ALLOW_NACL_SOCKET_API_DESCRIPTION +IDS_FLAGS_ALLOW_NACL_SOCKET_API_NAME +IDS_FLAGS_ALLOW_TOUCHPAD_THREE_FINGER_CLICK_DESCRIPTION +IDS_FLAGS_ALLOW_TOUCHPAD_THREE_FINGER_CLICK_NAME +IDS_FLAGS_COMPOSITED_LAYER_BORDERS +IDS_FLAGS_COMPOSITED_LAYER_BORDERS_DESCRIPTION +IDS_FLAGS_COMPOSITING_FOR_FIXED_POSITION_DESCRIPTION +IDS_FLAGS_COMPOSITING_FOR_FIXED_POSITION_HIGH_DPI +IDS_FLAGS_COMPOSITING_FOR_FIXED_POSITION_NAME +IDS_FLAGS_CONFLICTS_CHECK_DESCRIPTION +IDS_FLAGS_CONFLICTS_CHECK_NAME +IDS_FLAGS_DEBUG_PACKED_APP_DESCRIPTION +IDS_FLAGS_DEBUG_PACKED_APP_NAME +IDS_FLAGS_DEBUG_SHORTCUTS_DESCRIPTION +IDS_FLAGS_DEBUG_SHORTCUTS_NAME +IDS_FLAGS_DEFAULT_TILE_HEIGHT_DESCRIPTION +IDS_FLAGS_DEFAULT_TILE_HEIGHT_GRANDE +IDS_FLAGS_DEFAULT_TILE_HEIGHT_NAME +IDS_FLAGS_DEFAULT_TILE_HEIGHT_SHORT +IDS_FLAGS_DEFAULT_TILE_HEIGHT_TALL +IDS_FLAGS_DEFAULT_TILE_HEIGHT_VENTI +IDS_FLAGS_DEFAULT_TILE_WIDTH_DESCRIPTION +IDS_FLAGS_DEFAULT_TILE_WIDTH_GRANDE +IDS_FLAGS_DEFAULT_TILE_WIDTH_NAME +IDS_FLAGS_DEFAULT_TILE_WIDTH_SHORT +IDS_FLAGS_DEFAULT_TILE_WIDTH_TALL +IDS_FLAGS_DEFAULT_TILE_WIDTH_VENTI +IDS_FLAGS_DISABLE +IDS_FLAGS_DISABLE_ACCELERATED_2D_CANVAS_DESCRIPTION +IDS_FLAGS_DISABLE_ACCELERATED_2D_CANVAS_NAME +IDS_FLAGS_DISABLE_ACCELERATED_VIDEO_DECODE_DESCRIPTION +IDS_FLAGS_DISABLE_ACCELERATED_VIDEO_DECODE_NAME +IDS_FLAGS_DISABLE_BOOT_ANIMATION +IDS_FLAGS_DISABLE_BOOT_ANIMATION_DESCRIPTION +IDS_FLAGS_DISABLE_GESTURE_REQUIREMENT_FOR_MEDIA_PLAYBACK_DESCRIPTION +IDS_FLAGS_DISABLE_GESTURE_REQUIREMENT_FOR_MEDIA_PLAYBACK_NAME +IDS_FLAGS_DISABLE_HYPERLINK_AUDITING_DESCRIPTION +IDS_FLAGS_DISABLE_HYPERLINK_AUDITING_NAME +IDS_FLAGS_DISABLE_PNACL_DESCRIPTION +IDS_FLAGS_DISABLE_PNACL_NAME +IDS_FLAGS_DISABLE_SOFTWARE_RASTERIZER_DESCRIPTION +IDS_FLAGS_DISABLE_SOFTWARE_RASTERIZER_NAME +IDS_FLAGS_DISABLE_WEBGL_DESCRIPTION +IDS_FLAGS_DISABLE_WEBGL_NAME +IDS_FLAGS_DISABLE_WEBRTC_DESCRIPTION +IDS_FLAGS_DISABLE_WEBRTC_NAME +IDS_FLAGS_ENABLE +IDS_FLAGS_ENABLE_APPS_SHOW_ON_FIRST_PAINT_DESCRIPTION +IDS_FLAGS_ENABLE_APPS_SHOW_ON_FIRST_PAINT_NAME +IDS_FLAGS_ENABLE_CONTEXTUAL_SEARCH +IDS_FLAGS_ENABLE_CONTEXTUAL_SEARCH_DESCRIPTION +IDS_FLAGS_ENABLE_DEFERRED_IMAGE_DECODING_DESCRIPTION +IDS_FLAGS_ENABLE_DEFERRED_IMAGE_DECODING_NAME +IDS_FLAGS_ENABLE_DEVTOOLS_EXPERIMENTS_DESCRIPTION +IDS_FLAGS_ENABLE_DEVTOOLS_EXPERIMENTS_NAME +IDS_FLAGS_ENABLE_DOWNLOAD_RESUMPTION_DESCRIPTION +IDS_FLAGS_ENABLE_DOWNLOAD_RESUMPTION_NAME +IDS_FLAGS_ENABLE_ENHANCED_BOOKMARKS_DESCRIPTION +IDS_FLAGS_ENABLE_ENHANCED_BOOKMARKS_NAME +IDS_FLAGS_ENABLE_EXPERIMENTAL_CANVAS_FEATURES_DESCRIPTION +IDS_FLAGS_ENABLE_EXPERIMENTAL_CANVAS_FEATURES_NAME +IDS_FLAGS_ENABLE_GESTURE_TAP_HIGHLIGHTING_DESCRIPTION +IDS_FLAGS_ENABLE_GESTURE_TAP_HIGHLIGHTING_NAME +IDS_FLAGS_ENABLE_ICON_NTP_DESCRIPTION +IDS_FLAGS_ENABLE_ICON_NTP_NAME +IDS_FLAGS_ENABLE_JAVASCRIPT_HARMONY_DESCRIPTION +IDS_FLAGS_ENABLE_JAVASCRIPT_HARMONY_NAME +IDS_FLAGS_ENABLE_NACL_DEBUG_DESCRIPTION +IDS_FLAGS_ENABLE_NACL_DEBUG_NAME +IDS_FLAGS_ENABLE_NACL_DESCRIPTION +IDS_FLAGS_ENABLE_NACL_NAME +IDS_FLAGS_ENABLE_PANELS_DESCRIPTION +IDS_FLAGS_ENABLE_PANELS_NAME +IDS_FLAGS_ENABLE_PASSWORD_GENERATION_DESCRIPTION +IDS_FLAGS_ENABLE_PASSWORD_GENERATION_NAME +IDS_FLAGS_ENABLE_PINCH_SCALE_DESCRIPTION +IDS_FLAGS_ENABLE_PINCH_SCALE_NAME +IDS_FLAGS_ENABLE_REQUEST_TABLET_SITE_DESCRIPTION +IDS_FLAGS_ENABLE_REQUEST_TABLET_SITE_NAME +IDS_FLAGS_ENABLE_SCREEN_CAPTURE_DESCRIPTION +IDS_FLAGS_ENABLE_SCREEN_CAPTURE_NAME +IDS_FLAGS_ENABLE_SIMPLE_CACHE_BACKEND_DESCRIPTION +IDS_FLAGS_ENABLE_SIMPLE_CACHE_BACKEND_NAME +IDS_FLAGS_ENABLE_SMOOTH_SCROLLING_DESCRIPTION +IDS_FLAGS_ENABLE_SMOOTH_SCROLLING_NAME +IDS_FLAGS_ENABLE_STALE_WHILE_REVALIDATE_DESCRIPTION +IDS_FLAGS_ENABLE_STALE_WHILE_REVALIDATE_NAME +IDS_FLAGS_ENABLE_SUGGESTIONS_SERVICE_DESCRIPTION +IDS_FLAGS_ENABLE_SUGGESTIONS_SERVICE_NAME +IDS_FLAGS_ENABLE_SYNCED_NOTIFICATIONS_DESCRIPTION +IDS_FLAGS_ENABLE_SYNCED_NOTIFICATIONS_NAME +IDS_FLAGS_ENABLE_TCP_FAST_OPEN_DESCRIPTION +IDS_FLAGS_ENABLE_TCP_FAST_OPEN_NAME +IDS_FLAGS_ENABLE_TOUCH_DRAG_DROP_DESCRIPTION +IDS_FLAGS_ENABLE_TOUCH_DRAG_DROP_NAME +IDS_FLAGS_ENABLE_TOUCH_EDITING_DESCRIPTION +IDS_FLAGS_ENABLE_TOUCH_EDITING_NAME +IDS_FLAGS_ENABLE_TRANSLATE_NEW_UX_DESCRIPTION +IDS_FLAGS_ENABLE_TRANSLATE_NEW_UX_NAME +IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_DESCRIPTION +IDS_FLAGS_EXPERIMENTAL_EXTENSION_APIS_NAME +IDS_FLAGS_EXPERIMENTAL_WEB_PLATFORM_FEATURES_DESCRIPTION +IDS_FLAGS_EXPERIMENTAL_WEB_PLATFORM_FEATURES_NAME +IDS_FLAGS_EXTENSIONS_ON_CHROME_URLS_DESCRIPTION +IDS_FLAGS_EXTENSIONS_ON_CHROME_URLS_NAME +IDS_FLAGS_FORCE_ACCELERATED_OVERFLOW_SCROLL_MODE_DESCRIPTION +IDS_FLAGS_FORCE_ACCELERATED_OVERFLOW_SCROLL_MODE_NAME +IDS_FLAGS_FORCE_HIGH_DPI_DESCRIPTION +IDS_FLAGS_FORCE_HIGH_DPI_NAME +IDS_FLAGS_IGNORE_GPU_BLACKLIST_DESCRIPTION +IDS_FLAGS_IGNORE_GPU_BLACKLIST_NAME +IDS_FLAGS_LONG_TITLE +IDS_FLAGS_NACL_DEBUG_MASK_DESCRIPTION +IDS_FLAGS_NACL_DEBUG_MASK_NAME +IDS_FLAGS_NOT_AVAILABLE +IDS_FLAGS_NO_EXPERIMENTS_AVAILABLE +IDS_FLAGS_NO_UNSUPPORTED_EXPERIMENTS +IDS_FLAGS_NTP_OTHER_SESSIONS_MENU_DESCRIPTION +IDS_FLAGS_NTP_OTHER_SESSIONS_MENU_NAME +IDS_FLAGS_PERFORMANCE_MONITOR_GATHERING_DESCRIPTION +IDS_FLAGS_PERFORMANCE_MONITOR_GATHERING_NAME +IDS_FLAGS_RELAUNCH_BUTTON +IDS_FLAGS_RELAUNCH_NOTICE +IDS_FLAGS_RESET_ALL_BUTTON +IDS_FLAGS_SAVE_PAGE_AS_MHTML_DESCRIPTION +IDS_FLAGS_SAVE_PAGE_AS_MHTML_NAME +IDS_FLAGS_SHOW_AUTOFILL_TYPE_PREDICTIONS_DESCRIPTION +IDS_FLAGS_SHOW_AUTOFILL_TYPE_PREDICTIONS_NAME +IDS_FLAGS_SHOW_FPS_COUNTER +IDS_FLAGS_SHOW_FPS_COUNTER_DESCRIPTION +IDS_FLAGS_SHOW_TOUCH_HUD_DESCRIPTION +IDS_FLAGS_SHOW_TOUCH_HUD_NAME +IDS_FLAGS_SILENT_DEBUGGER_EXTENSION_API_DESCRIPTION +IDS_FLAGS_SILENT_DEBUGGER_EXTENSION_API_NAME +IDS_FLAGS_SPELLCHECK_AUTOCORRECT +IDS_FLAGS_SPELLCHECK_AUTOCORRECT_DESCRIPTION +IDS_FLAGS_STACKED_TAB_STRIP_DESCRIPTION +IDS_FLAGS_STACKED_TAB_STRIP_NAME +IDS_FLAGS_TABLE_TITLE +IDS_FLAGS_THREADED_COMPOSITING_MODE_DESCRIPTION +IDS_FLAGS_THREADED_COMPOSITING_MODE_NAME +IDS_FLAGS_TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE +IDS_FLAGS_TOUCH_SCROLLING_MODE_DESCRIPTION +IDS_FLAGS_TOUCH_SCROLLING_MODE_NAME +IDS_FLAGS_TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE +IDS_FLAGS_TOUCH_SCROLLING_MODE_TOUCHCANCEL +IDS_FLAGS_UNSUPPORTED_TABLE_TITLE +IDS_FLAGS_WALLET_SERVICE_USE_SANDBOX_DESCRIPTION +IDS_FLAGS_WALLET_SERVICE_USE_SANDBOX_NAME +IDS_FLAGS_WARNING_HEADER +IDS_FLAGS_WARNING_TEXT +IDS_FULLSCREEN +IDS_GENERIC_EXPERIMENT_CHOICE_AUTOMATIC +IDS_GENERIC_EXPERIMENT_CHOICE_DEFAULT +IDS_GENERIC_EXPERIMENT_CHOICE_DISABLED +IDS_GENERIC_EXPERIMENT_CHOICE_ENABLED +IDS_GROUP_BY_DOMAIN_LABEL +IDS_GUEST_PROFILE_NAME +IDS_HARMFUL_V3_EXPLANATION_PARAGRAPH +IDS_HARMFUL_V3_HEADING +IDS_HARMFUL_V3_PRIMARY_PARAGRAPH +IDS_HARMFUL_V3_PROCEED_PARAGRAPH +IDS_HISTORY_ACTION_MENU_DESCRIPTION +IDS_HISTORY_BLOCKED_VISIT_TEXT +IDS_HISTORY_BROWSERESULTS +IDS_HISTORY_CONTINUED +IDS_HISTORY_DATE_WITH_RELATIVE_TIME +IDS_HISTORY_DELETE_PRIOR_VISITS_CONFIRM_BUTTON +IDS_HISTORY_DELETE_PRIOR_VISITS_WARNING +IDS_HISTORY_FILTER_ALLOWED +IDS_HISTORY_FILTER_ALLOW_ITEMS +IDS_HISTORY_FILTER_BLOCKED +IDS_HISTORY_FILTER_BLOCK_ITEMS +IDS_HISTORY_HAS_SYNCED_RESULTS +IDS_HISTORY_INTERVAL +IDS_HISTORY_IN_CONTENT_PACK +IDS_HISTORY_LOADING +IDS_HISTORY_LOCK_BUTTON +IDS_HISTORY_MORE_FROM_SITE +IDS_HISTORY_NEWER +IDS_HISTORY_NEWEST +IDS_HISTORY_NO_RESULTS +IDS_HISTORY_NO_SEARCH_RESULTS +IDS_HISTORY_NO_SYNCED_RESULTS +IDS_HISTORY_NUMBER_VISITS +IDS_HISTORY_OLDER +IDS_HISTORY_OPEN_CLEAR_BROWSING_DATA_DIALOG +IDS_HISTORY_OTHER_SESSIONS_COLLAPSE_SESSION +IDS_HISTORY_OTHER_SESSIONS_EXPAND_SESSION +IDS_HISTORY_OTHER_SESSIONS_OPEN_ALL +IDS_HISTORY_RANGE_ALL_TIME +IDS_HISTORY_RANGE_LABEL +IDS_HISTORY_RANGE_MONTH +IDS_HISTORY_RANGE_NEXT +IDS_HISTORY_RANGE_PREVIOUS +IDS_HISTORY_RANGE_TODAY +IDS_HISTORY_RANGE_WEEK +IDS_HISTORY_REMOVE_BOOKMARK +IDS_HISTORY_REMOVE_PAGE +IDS_HISTORY_REMOVE_SELECTED_ITEMS +IDS_HISTORY_SEARCHRESULTSFOR +IDS_HISTORY_SEARCH_BUTTON +IDS_HISTORY_TITLE +IDS_HISTORY_UNKNOWN_DEVICE +IDS_HISTORY_UNLOCK_BUTTON +IDS_HTTP_POST_WARNING +IDS_HTTP_POST_WARNING_RESEND +IDS_HTTP_POST_WARNING_TITLE +IDS_IMPORT_FROM_FIREFOX +IDS_IMPORT_FROM_ICEWEASEL +IDS_JAVASCRIPT_ALERT_DEFAULT_TITLE +IDS_JAVASCRIPT_ALERT_TITLE +IDS_JAVASCRIPT_MESSAGEBOX_DEFAULT_TITLE +IDS_JAVASCRIPT_MESSAGEBOX_TITLE +IDS_KEYWORD_SEARCH +IDS_LEARN_MORE +IDS_LEGACY_DEFAULT_PROFILE_NAME +IDS_LIBADDRESSINPUT_ADDRESS_LINE_1_LABEL +IDS_LIBADDRESSINPUT_AREA +IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL +IDS_LIBADDRESSINPUT_COUNTY +IDS_LIBADDRESSINPUT_DEPARTMENT +IDS_LIBADDRESSINPUT_DISTRICT +IDS_LIBADDRESSINPUT_DO_SI +IDS_LIBADDRESSINPUT_EMIRATE +IDS_LIBADDRESSINPUT_ISLAND +IDS_LIBADDRESSINPUT_LOCALITY_LABEL +IDS_LIBADDRESSINPUT_MISMATCHING_VALUE_POSTAL_CODE +IDS_LIBADDRESSINPUT_MISMATCHING_VALUE_POSTAL_CODE_URL +IDS_LIBADDRESSINPUT_MISMATCHING_VALUE_ZIP +IDS_LIBADDRESSINPUT_MISMATCHING_VALUE_ZIP_URL +IDS_LIBADDRESSINPUT_MISSING_REQUIRED_FIELD +IDS_LIBADDRESSINPUT_MISSING_REQUIRED_POSTAL_CODE_EXAMPLE +IDS_LIBADDRESSINPUT_MISSING_REQUIRED_POSTAL_CODE_EXAMPLE_AND_URL +IDS_LIBADDRESSINPUT_MISSING_REQUIRED_ZIP_CODE_EXAMPLE +IDS_LIBADDRESSINPUT_MISSING_REQUIRED_ZIP_CODE_EXAMPLE_AND_URL +IDS_LIBADDRESSINPUT_NEIGHBORHOOD +IDS_LIBADDRESSINPUT_OBLAST +IDS_LIBADDRESSINPUT_ORGANIZATION_LABEL +IDS_LIBADDRESSINPUT_PARISH +IDS_LIBADDRESSINPUT_PIN_CODE_LABEL +IDS_LIBADDRESSINPUT_POSTAL_CODE_LABEL +IDS_LIBADDRESSINPUT_POST_TOWN +IDS_LIBADDRESSINPUT_PO_BOX_FORBIDDEN_VALUE +IDS_LIBADDRESSINPUT_PREFECTURE +IDS_LIBADDRESSINPUT_PROVINCE +IDS_LIBADDRESSINPUT_RECIPIENT_LABEL +IDS_LIBADDRESSINPUT_STATE +IDS_LIBADDRESSINPUT_SUBURB +IDS_LIBADDRESSINPUT_UNKNOWN_VALUE +IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_POSTAL_CODE +IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_POSTAL_CODE_EXAMPLE +IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_POSTAL_CODE_EXAMPLE_AND_URL +IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP +IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP_CODE_EXAMPLE +IDS_LIBADDRESSINPUT_UNRECOGNIZED_FORMAT_ZIP_CODE_EXAMPLE_AND_URL +IDS_LIBADDRESSINPUT_VILLAGE_TOWNSHIP +IDS_LIBADDRESSINPUT_ZIP_CODE_LABEL +IDS_LINK_FROM_CLIPBOARD +IDS_LOGIN_DIALOG_OK_BUTTON_LABEL +IDS_LOGIN_DIALOG_PASSWORD_FIELD +IDS_LOGIN_DIALOG_TITLE +IDS_LOGIN_DIALOG_USERNAME_FIELD +IDS_MALWARE_V3_ADVICE_HEADING +IDS_MALWARE_V3_EXPLANATION_PARAGRAPH +IDS_MALWARE_V3_EXPLANATION_PARAGRAPH_ADVICE +IDS_MALWARE_V3_EXPLANATION_PARAGRAPH_HISTORY +IDS_MALWARE_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE +IDS_MALWARE_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE_ADVICE +IDS_MALWARE_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE_HISTORY +IDS_MALWARE_V3_HEADING +IDS_MALWARE_V3_PRIMARY_PARAGRAPH +IDS_MALWARE_V3_PROCEED_PARAGRAPH +IDS_MALWARE_V3_PROCEED_PARAGRAPH_NOT_RECOMMEND +IDS_MALWARE_V3_PROCEED_PARAGRAPH_SOCIAL +IDS_MANAGED_USER_AVATAR_LABEL +IDS_MIDI_SYSEX_INFOBAR_QUESTION +IDS_MIDI_SYSEX_PERMISSION_FRAGMENT +IDS_MOBILE_WELCOME_URL +IDS_NACL_DEBUG_MASK_CHOICE_DEBUG_ALL +IDS_NACL_DEBUG_MASK_CHOICE_EXCLUDE_UTILS_PNACL +IDS_NACL_DEBUG_MASK_CHOICE_INCLUDE_DEBUG +IDS_NETWORK_PREDICTION_ENABLED_DESCRIPTION +IDS_NET_EXPORT_NO_EMAIL_ACCOUNTS_ALERT_MESSAGE +IDS_NET_EXPORT_NO_EMAIL_ACCOUNTS_ALERT_TITLE +IDS_NEW_INCOGNITO_WINDOW_MAC +IDS_NEW_NUMBERED_PROFILE_NAME +IDS_NEW_TAB_CHROME_WELCOME_PAGE_TITLE +IDS_NEW_TAB_MOST_VISITED +IDS_NEW_TAB_RECENTLY_CLOSED +IDS_NEW_TAB_RESTORE_THUMBNAILS_SHORT_LINK +IDS_NEW_TAB_THUMBNAIL_REMOVED_NOTIFICATION +IDS_NEW_TAB_TITLE +IDS_NEW_TAB_UNDO_THUMBNAIL_REMOVE +IDS_NUMBERED_PROFILE_NAME +IDS_OK +IDS_OMNIBOX_EMPTY_HINT +IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_CANCEL_BUTTON +IDS_OPEN_TABS_NOTYETSYNCED +IDS_OPEN_TABS_PROMOCOMPUTER +IDS_OPTIONS_ADVANCED_SECTION_TITLE_PRIVACY +IDS_OPTIONS_DISABLE_WEB_SERVICES +IDS_OPTIONS_ENABLE_LOGGING +IDS_OPTIONS_IMPROVE_BROWSING_EXPERIENCE +IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON +IDS_OTHER_DEVICES_X_MORE +IDS_PAGEINFO_ADDRESS +IDS_PAGEINFO_CERT_INFO_BUTTON +IDS_PAGEINFO_PARTIAL_ADDRESS +IDS_PAGE_INFO_HELP_CENTER_LINK +IDS_PAGE_INFO_INTERNAL_PAGE +IDS_PAGE_INFO_SECURITY_BUTTON_ACCESSIBILITY_LABEL +IDS_PAGE_INFO_SECURITY_TAB_DEPRECATED_SIGNATURE_ALGORITHM +IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_CONNECTION_TEXT +IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_INSECURE_CONTENT_ERROR +IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_INSECURE_CONTENT_WARNING +IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_SENTENCE_LINK +IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTION_DETAILS +IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTION_DETAILS_AEAD +IDS_PAGE_INFO_SECURITY_TAB_FALLBACK_MESSAGE +IDS_PAGE_INFO_SECURITY_TAB_FIRST_VISITED_TODAY +IDS_PAGE_INFO_SECURITY_TAB_INSECURE_IDENTITY +IDS_PAGE_INFO_SECURITY_TAB_NON_UNIQUE_NAME +IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT +IDS_PAGE_INFO_SECURITY_TAB_NO_REVOCATION_MECHANISM +IDS_PAGE_INFO_SECURITY_TAB_RENEGOTIATION_MESSAGE +IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_EV_NO_CT +IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY_NO_CT +IDS_PAGE_INFO_SECURITY_TAB_SSL_VERSION +IDS_PAGE_INFO_SECURITY_TAB_UNABLE_TO_CHECK_REVOCATION +IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY +IDS_PAGE_INFO_SECURITY_TAB_VISITED_BEFORE_TODAY +IDS_PAGE_INFO_SECURITY_TAB_WEAK_ENCRYPTION_CONNECTION_TEXT +IDS_PAGE_INFO_SITE_INFO_TITLE +IDS_PASSWORDS_EXCEPTIONS_TAB_TITLE +IDS_PASSWORDS_SHOW_PASSWORDS_TAB_TITLE +IDS_PASSWORD_MANAGER_BLACKLIST_BUTTON +IDS_PASSWORD_MANAGER_EMPTY_LOGIN +IDS_PASSWORD_MANAGER_SAVE_BUTTON +IDS_PASSWORD_MANAGER_SAVE_PASSWORD_PROMPT +IDS_PAST_TIME_TODAY +IDS_PAST_TIME_YESTERDAY +IDS_PDF_INFOBAR_ALWAYS_USE_READER_BUTTON +IDS_PERMISSION_ALLOW +IDS_PERMISSION_DENY +IDS_PHISHING_V3_EXPLANATION_PARAGRAPH +IDS_PHISHING_V3_HEADING +IDS_PHISHING_V3_PRIMARY_PARAGRAPH +IDS_PHISHING_V3_PROCEED_PARAGRAPH +IDS_PLATFORM_LABEL +IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_ACCEPT_BUTTON +IDS_PLUGIN_CONFIRM_INSTALL_DIALOG_TITLE +IDS_PLUGIN_NOT_SUPPORTED +IDS_POLICY_ASSOCIATION_STATE_ACTIVE +IDS_POLICY_ASSOCIATION_STATE_DEPROVISIONED +IDS_POLICY_ASSOCIATION_STATE_UNMANAGED +IDS_POLICY_DEFAULT_SEARCH_DISABLED +IDS_POLICY_DEPRECATED +IDS_POLICY_DM_STATUS_HTTP_STATUS_ERROR +IDS_POLICY_DM_STATUS_REQUEST_FAILED +IDS_POLICY_DM_STATUS_REQUEST_INVALID +IDS_POLICY_DM_STATUS_RESPONSE_DECODING_ERROR +IDS_POLICY_DM_STATUS_SERVICE_ACTIVATION_PENDING +IDS_POLICY_DM_STATUS_SERVICE_DEPROVISIONED +IDS_POLICY_DM_STATUS_SERVICE_DEVICE_ID_CONFLICT +IDS_POLICY_DM_STATUS_SERVICE_DEVICE_NOT_FOUND +IDS_POLICY_DM_STATUS_SERVICE_DOMAIN_MISMATCH +IDS_POLICY_DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER +IDS_POLICY_DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED +IDS_POLICY_DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID +IDS_POLICY_DM_STATUS_SERVICE_MISSING_LICENSES +IDS_POLICY_DM_STATUS_SERVICE_POLICY_NOT_FOUND +IDS_POLICY_DM_STATUS_SUCCESS +IDS_POLICY_DM_STATUS_TEMPORARY_UNAVAILABLE +IDS_POLICY_DM_STATUS_UNKNOWN_ERROR +IDS_POLICY_FILTER_PLACEHOLDER +IDS_POLICY_HEADER_LEVEL +IDS_POLICY_HEADER_NAME +IDS_POLICY_HEADER_SCOPE +IDS_POLICY_HEADER_STATUS +IDS_POLICY_HEADER_VALUE +IDS_POLICY_HIDE_EXPANDED_VALUE +IDS_POLICY_INVALID_BOOKMARK +IDS_POLICY_INVALID_PROXY_MODE_ERROR +IDS_POLICY_INVALID_SEARCH_URL_ERROR +IDS_POLICY_LABEL_ASSET_ID +IDS_POLICY_LABEL_CLIENT_ID +IDS_POLICY_LABEL_DIRECTORY_API_ID +IDS_POLICY_LABEL_DOMAIN +IDS_POLICY_LABEL_LOCATION +IDS_POLICY_LABEL_REFRESH_INTERVAL +IDS_POLICY_LABEL_STATUS +IDS_POLICY_LABEL_TIME_SINCE_LAST_REFRESH +IDS_POLICY_LABEL_USERNAME +IDS_POLICY_LEVEL_ERROR +IDS_POLICY_LEVEL_MANDATORY +IDS_POLICY_LEVEL_RECOMMENDED +IDS_POLICY_LIST_ENTRY_ERROR +IDS_POLICY_NEVER_FETCHED +IDS_POLICY_NOT_SPECIFIED +IDS_POLICY_NOT_SPECIFIED_ERROR +IDS_POLICY_NO_POLICIES_SET +IDS_POLICY_OK +IDS_POLICY_OUT_OF_RANGE_ERROR +IDS_POLICY_OVERRIDDEN +IDS_POLICY_PROXY_BOTH_SPECIFIED_ERROR +IDS_POLICY_PROXY_MODE_AUTO_DETECT_ERROR +IDS_POLICY_PROXY_MODE_DISABLED_ERROR +IDS_POLICY_PROXY_MODE_FIXED_SERVERS_ERROR +IDS_POLICY_PROXY_MODE_PAC_URL_ERROR +IDS_POLICY_PROXY_MODE_SYSTEM_ERROR +IDS_POLICY_PROXY_NEITHER_SPECIFIED_ERROR +IDS_POLICY_RELOAD_POLICIES +IDS_POLICY_SCHEMA_VALIDATION_ERROR +IDS_POLICY_SCOPE_DEVICE +IDS_POLICY_SCOPE_USER +IDS_POLICY_SHOW_EXPANDED_VALUE +IDS_POLICY_SHOW_UNSET +IDS_POLICY_STATUS +IDS_POLICY_STATUS_DEVICE +IDS_POLICY_STATUS_USER +IDS_POLICY_STORE_STATUS_BAD_STATE +IDS_POLICY_STORE_STATUS_LOAD_ERROR +IDS_POLICY_STORE_STATUS_OK +IDS_POLICY_STORE_STATUS_PARSE_ERROR +IDS_POLICY_STORE_STATUS_SERIALIZE_ERROR +IDS_POLICY_STORE_STATUS_STORE_ERROR +IDS_POLICY_STORE_STATUS_UNKNOWN_ERROR +IDS_POLICY_STORE_STATUS_VALIDATION_ERROR +IDS_POLICY_SUBKEY_ERROR +IDS_POLICY_TITLE +IDS_POLICY_TYPE_ERROR +IDS_POLICY_UNKNOWN +IDS_POLICY_UNSET +IDS_POLICY_VALIDATION_BAD_INITIAL_SIGNATURE +IDS_POLICY_VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE +IDS_POLICY_VALIDATION_BAD_SIGNATURE +IDS_POLICY_VALIDATION_BAD_TIMESTAMP +IDS_POLICY_VALIDATION_BAD_USERNAME +IDS_POLICY_VALIDATION_ERROR_CODE_PRESENT +IDS_POLICY_VALIDATION_OK +IDS_POLICY_VALIDATION_PAYLOAD_PARSE_ERROR +IDS_POLICY_VALIDATION_POLICY_PARSE_ERROR +IDS_POLICY_VALIDATION_UNKNOWN_ERROR +IDS_POLICY_VALIDATION_WRONG_POLICY_TYPE +IDS_POLICY_VALIDATION_WRONG_SETTINGS_ENTITY_ID +IDS_POLICY_VALIDATION_WRONG_TOKEN +IDS_PREFERENCES_CORRUPT_ERROR +IDS_PREFERENCES_UNREADABLE_ERROR +IDS_PRINT +IDS_PRIVACY_POLICY_URL +IDS_PRODUCT_NAME +IDS_PROFILES_GUEST_PROFILE_NAME +IDS_PROFILES_LOCAL_PROFILE_STATE +IDS_PROFILE_TOO_NEW_ERROR +IDS_PUSH_MESSAGES_BUBBLE_FRAGMENT +IDS_PUSH_MESSAGES_BUBBLE_TEXT +IDS_PUSH_MESSAGES_PERMISSION_QUESTION +IDS_RECENT_TABS_MENU +IDS_SAD_TAB_MESSAGE +IDS_SAD_TAB_RELOAD_LABEL +IDS_SAD_TAB_TITLE +IDS_SAFEBROWSING_OVERRIDABLE_SAFETY_BUTTON +IDS_SAFEBROWSING_V3_CLOSE_DETAILS_BUTTON +IDS_SAFEBROWSING_V3_OPEN_DETAILS_BUTTON +IDS_SAFEBROWSING_V3_TITLE +IDS_SAFE_BROWSING_MALWARE_BACK_BUTTON +IDS_SAFE_BROWSING_MALWARE_BACK_HEADLINE +IDS_SAFE_BROWSING_MALWARE_COLLAB_HEADLINE +IDS_SAFE_BROWSING_MALWARE_DIAGNOSTIC_PAGE +IDS_SAFE_BROWSING_MALWARE_FEAR_HEADLINE +IDS_SAFE_BROWSING_MALWARE_HEADLINE +IDS_SAFE_BROWSING_MALWARE_LABEL +IDS_SAFE_BROWSING_MALWARE_QUESTION_HEADLINE +IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE +IDS_SAFE_BROWSING_MALWARE_TITLE +IDS_SAFE_BROWSING_MALWARE_V2_DESCRIPTION1 +IDS_SAFE_BROWSING_MALWARE_V2_DESCRIPTION1_SUBRESOURCE +IDS_SAFE_BROWSING_MALWARE_V2_DESCRIPTION2 +IDS_SAFE_BROWSING_MALWARE_V2_DESCRIPTION2_SUBRESOURCE +IDS_SAFE_BROWSING_MALWARE_V2_DESCRIPTION3 +IDS_SAFE_BROWSING_MALWARE_V2_DETAILS +IDS_SAFE_BROWSING_MALWARE_V2_DETAILS_SUBRESOURCE +IDS_SAFE_BROWSING_MALWARE_V2_HEADLINE +IDS_SAFE_BROWSING_MALWARE_V2_HEADLINE_SUBRESOURCE +IDS_SAFE_BROWSING_MALWARE_V2_LEARN_MORE +IDS_SAFE_BROWSING_MALWARE_V2_PROCEED_LINK +IDS_SAFE_BROWSING_MALWARE_V2_REPORTING_AGREE +IDS_SAFE_BROWSING_MALWARE_V2_SEE_MORE +IDS_SAFE_BROWSING_MALWARE_V2_TITLE +IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION1 +IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION2 +IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION3 +IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION_AGREE +IDS_SAFE_BROWSING_MULTI_MALWARE_PROCEED_BUTTON +IDS_SAFE_BROWSING_MULTI_PHISHING_DESCRIPTION1 +IDS_SAFE_BROWSING_MULTI_THREAT_DESCRIPTION1 +IDS_SAFE_BROWSING_MULTI_THREAT_DESCRIPTION2 +IDS_SAFE_BROWSING_MULTI_THREAT_TITLE +IDS_SAFE_BROWSING_PHISHING_BACK_HEADLINE +IDS_SAFE_BROWSING_PHISHING_COLLAB_HEADLINE +IDS_SAFE_BROWSING_PHISHING_FEAR_HEADLINE +IDS_SAFE_BROWSING_PHISHING_HEADLINE +IDS_SAFE_BROWSING_PHISHING_LABEL +IDS_SAFE_BROWSING_PHISHING_QUESTION_HEADLINE +IDS_SAFE_BROWSING_PHISHING_REPORT_ERROR +IDS_SAFE_BROWSING_PHISHING_TITLE +IDS_SAFE_BROWSING_PHISHING_V2_DESCRIPTION1 +IDS_SAFE_BROWSING_PHISHING_V2_DESCRIPTION2 +IDS_SAFE_BROWSING_PHISHING_V2_HEADLINE +IDS_SAFE_BROWSING_PHISHING_V2_REPORT_ERROR +IDS_SAFE_BROWSING_PHISHING_V2_TITLE +IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE +IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE_V2 +IDS_SAFE_BROWSING_PRIVACY_POLICY_URL +IDS_SAVE +IDS_SEARCH_BOX_EMPTY_HINT +IDS_SECURE_CONNECTION_EV +IDS_SESSION_CRASHED_VIEW_MESSAGE +IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON +IDS_SETTINGS_SHOW_ADVANCED_SETTINGS +IDS_SHORT_PRODUCT_NAME +IDS_SHOW_HISTORY +IDS_SIGNED_IN_WITH_SYNC_DISABLED +IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED +IDS_SIGNIN_ERROR_BUBBLE_VIEW_TITLE +IDS_SINGLE_PROFILE_DISPLAY_NAME +IDS_SSL_CLOCK_ERROR +IDS_SSL_CLOCK_ERROR_EXPLANATION +IDS_SSL_NONOVERRIDABLE_HSTS +IDS_SSL_NONOVERRIDABLE_INVALID +IDS_SSL_NONOVERRIDABLE_MORE +IDS_SSL_NONOVERRIDABLE_MORE_INVALID_SP3 +IDS_SSL_NONOVERRIDABLE_PINNED +IDS_SSL_NONOVERRIDABLE_REVOKED +IDS_SSL_OVERRIDABLE_PRIMARY_PARAGRAPH +IDS_SSL_OVERRIDABLE_PROCEED_LINK_TEXT +IDS_SSL_OVERRIDABLE_PROCEED_PARAGRAPH +IDS_SSL_OVERRIDABLE_SAFETY_BUTTON +IDS_SSL_OVERRIDABLE_TITLE +IDS_SSL_RELOAD +IDS_SSL_V2_CLOCK_AHEAD_HEADING +IDS_SSL_V2_CLOCK_BEHIND_HEADING +IDS_SSL_V2_CLOCK_PRIMARY_PARAGRAPH +IDS_SSL_V2_CLOCK_TITLE +IDS_SSL_V2_CLOCK_UPDATE_DATE_AND_TIME +IDS_SSL_V2_CLOSE_DETAILS_BUTTON +IDS_SSL_V2_HEADING +IDS_SSL_V2_OPEN_DETAILS_BUTTON +IDS_SSL_V2_PRIMARY_PARAGRAPH +IDS_SSL_V2_TITLE +IDS_STARS_PROMO_LABEL_IOS +IDS_SUPERVISED_USER_AVATAR_LABEL +IDS_SUPERVISED_USER_NEW_AVATAR_LABEL +IDS_SYNC_ACCOUNT_DETAILS_NOT_ENTERED +IDS_SYNC_ACCOUNT_SYNCING_TO_USER +IDS_SYNC_ACCOUNT_SYNCING_TO_USER_WITH_MANAGE_LINK +IDS_SYNC_AUTHENTICATING_LABEL +IDS_SYNC_BASIC_ENCRYPTION_DATA +IDS_SYNC_CLEAR_USER_DATA +IDS_SYNC_CONFIGURE_ENCRYPTION +IDS_SYNC_DATATYPE_AUTOFILL +IDS_SYNC_DATATYPE_BOOKMARKS +IDS_SYNC_DATATYPE_PASSWORDS +IDS_SYNC_DATATYPE_PREFERENCES +IDS_SYNC_DATATYPE_TABS +IDS_SYNC_DATATYPE_TYPED_URLS +IDS_SYNC_EMPTY_PASSPHRASE_ERROR +IDS_SYNC_ENABLE_SYNC_ON_ACCOUNT +IDS_SYNC_ENCRYPTION_SECTION_TITLE +IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY +IDS_SYNC_ENTER_PASSPHRASE_BODY +IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE +IDS_SYNC_ENTER_PASSPHRASE_TITLE +IDS_SYNC_ERROR_BUBBLE_VIEW_TITLE +IDS_SYNC_ERROR_SIGNING_IN +IDS_SYNC_FULL_ENCRYPTION_DATA +IDS_SYNC_INVALID_USER_CREDENTIALS +IDS_SYNC_LOGIN_INFO_OUT_OF_DATE +IDS_SYNC_LOGIN_SETTING_UP +IDS_SYNC_MENU_PRE_SYNCED_LABEL +IDS_SYNC_MENU_SYNCED_LABEL +IDS_SYNC_NTP_PASSWORD_ENABLE +IDS_SYNC_NTP_PASSWORD_PROMO +IDS_SYNC_NTP_PASSWORD_PROMO, +IDS_SYNC_NTP_SETUP_IN_PROGRESS +IDS_SYNC_OPTIONS_GROUP_NAME +IDS_SYNC_OTHER_SIGN_IN_ERROR_BUBBLE_VIEW_MESSAGE +IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_ACCEPT +IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_MESSAGE +IDS_SYNC_PASSPHRASE_ERROR_WRENCH_MENU_ITEM +IDS_SYNC_PASSPHRASE_LABEL +IDS_SYNC_PASSPHRASE_MISMATCH_ERROR +IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_POSTFIX +IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_PREFIX +IDS_SYNC_PASSWORD_SYNC_ATTENTION +IDS_SYNC_PROMO_NTP_BUBBLE_MESSAGE +IDS_SYNC_PROMO_TAB_TITLE +IDS_SYNC_RELOGIN_LINK_LABEL +IDS_SYNC_SERVER_IS_UNREACHABLE +IDS_SYNC_SERVICE_UNAVAILABLE +IDS_SYNC_SETUP_ERROR +IDS_SYNC_SIGN_IN_ERROR_BUBBLE_VIEW_ACCEPT +IDS_SYNC_SIGN_IN_ERROR_BUBBLE_VIEW_MESSAGE +IDS_SYNC_SIGN_IN_ERROR_WRENCH_MENU_ITEM +IDS_SYNC_START_SYNC_BUTTON_LABEL +IDS_SYNC_STATUS_UNRECOVERABLE_ERROR +IDS_SYNC_STOP_AND_RESTART_SYNC +IDS_SYNC_TIME_JUST_NOW +IDS_SYNC_TIME_NEVER +IDS_SYNC_UNAVAILABLE_ERROR_BUBBLE_VIEW_ACCEPT +IDS_SYNC_UNAVAILABLE_ERROR_BUBBLE_VIEW_MESSAGE +IDS_SYNC_UNRECOVERABLE_ERROR_HELP_URL +IDS_SYNC_UPGRADE_CLIENT +IDS_SYSTEM_FLAGS_OWNER_ONLY +IDS_TERMS_HTML +IDS_TIME_DAYS +IDS_TIME_DAYS_1ST +IDS_TIME_ELAPSED_DAYS +IDS_TIME_ELAPSED_HOURS +IDS_TIME_ELAPSED_MINS +IDS_TIME_ELAPSED_SECS +IDS_TIME_HOURS +IDS_TIME_HOURS_1ST +IDS_TIME_HOURS_2ND +IDS_TIME_LONG_MINS +IDS_TIME_LONG_MINS_1ST +IDS_TIME_LONG_MINS_2ND +IDS_TIME_LONG_SECS +IDS_TIME_LONG_SECS_2ND +IDS_TIME_MINS +IDS_TIME_REMAINING_DAYS +IDS_TIME_REMAINING_HOURS +IDS_TIME_REMAINING_LONG_MINS +IDS_TIME_REMAINING_LONG_SECS +IDS_TIME_REMAINING_MINS +IDS_TIME_REMAINING_SECS +IDS_TIME_SECS +IDS_TOOLTIP_STAR +IDS_TOUCH_EVENTS_DESCRIPTION +IDS_TOUCH_EVENTS_NAME +IDS_TRANSLATE_INFOBAR_ACCEPT +IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE +IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE_AUTODETERMINED_SOURCE_LANGUAGE +IDS_TRANSLATE_INFOBAR_ALWAYS_TRANSLATE +IDS_TRANSLATE_INFOBAR_BEFORE_MESSAGE +IDS_TRANSLATE_INFOBAR_BEFORE_MESSAGE_IOS +IDS_TRANSLATE_INFOBAR_DENY +IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT +IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE +IDS_TRANSLATE_INFOBAR_ERROR_SAME_LANGUAGE +IDS_TRANSLATE_INFOBAR_NEVER_MESSAGE_IOS +IDS_TRANSLATE_INFOBAR_NEVER_TRANSLATE +IDS_TRANSLATE_INFOBAR_OPTIONS_ABOUT +IDS_TRANSLATE_INFOBAR_OPTIONS_ALWAYS +IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_LANG +IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_SITE +IDS_TRANSLATE_INFOBAR_OPTIONS_REPORT_ERROR +IDS_TRANSLATE_INFOBAR_RETRY +IDS_TRANSLATE_INFOBAR_REVERT +IDS_TRANSLATE_INFOBAR_TRANSLATING_TO +IDS_TRANSLATE_INFOBAR_UNKNOWN_PAGE_LANGUAGE +IDS_TRANSLATE_INFOBAR_UNSUPPORTED_PAGE_LANGUAGE +IDS_UPGRADE_AVAILABLE +IDS_UPGRADE_AVAILABLE_BUTTON +IDS_WEB_FONT_FAMILY +IDS_WEB_FONT_SIZE diff --git a/engine/src/flutter/build/json_schema_api.gni b/engine/src/flutter/build/json_schema_api.gni new file mode 100644 index 0000000000..e1c2d33409 --- /dev/null +++ b/engine/src/flutter/build/json_schema_api.gni @@ -0,0 +1,242 @@ +# Copyright 2014 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. + +# Defines a static library corresponding to the output of schema compiler tools +# over a set of extensions API schemas (IDL or JSON format.) The library target +# has implicit hard dependencies on all schema files listed by the invoker and +# is itself a hard dependency. +# +# Invocations of this template may use the following variables: +# +# sources [required] A list of schema files to be compiled. +# +# root_namespace [required] +# A Python string substituion pattern used to generate the C++ +# namespace for each API. Use %(namespace)s to replace with the API +# namespace, like "toplevel::%(namespace)s_api". +# +# schema_include_rules [optional] +# A list of paths to include when searching for referenced objects, +# with the namespace separated by a :. +# Example: +# [ '/foo/bar:Foo::Bar::%(namespace)s' ] +# +# schemas [optional, default = false] +# Boolean indicating if the schema files should be generated. +# +# bundle [optional, default = false] +# Boolean indicating if the schema bundle files should be generated. +# +# bundle_registration [optional, default = false] +# Boolean indicating if the API registration bundle files should be generated. +# +# impl_dir [required if bundle_registration = true, otherwise unused] +# The path containing C++ implementations of API functions. This path is +# used as the root path when looking for {schema}/{schema}_api.h headers +# when generating API registration bundles. Such headers, if found, are +# automatically included by the generated code. +# +# uncompiled_sources [optional, only used when bundle = true or +# bundle_registration = true] +# A list of schema files which should not be compiled, but which should still +# be processed for API bundle generation. +# +# deps [optional] +# If any deps are specified they will be inherited by the static library +# target. +# +# generate_static_library [optional, defaults to false] +# Produces a static library instead of a source_set. +# +# The generated library target also inherits the visibility and output_name +# of its invoker. + +template("json_schema_api") { + assert(defined(invoker.sources), + "\"sources\" must be defined for the $target_name template.") + assert(defined(invoker.root_namespace), + "\"root_namespace\" must be defined for the $target_name template.") + + schemas = defined(invoker.schemas) && invoker.schemas + bundle = defined(invoker.bundle) && invoker.bundle + bundle_registration = + defined(invoker.bundle_registration) && invoker.bundle_registration + + schema_include_rules = "" + if (defined(invoker.schema_include_rules)) { + schema_include_rules = invoker.schema_include_rules + } + + # Keep a copy of the target_name here since it will be trampled + # in nested targets. + target_visibility = [ ":$target_name" ] + + generated_config_name = target_name + "_generated_config" + config(generated_config_name) { + include_dirs = [ root_gen_dir ] + visibility = target_visibility + } + + root_namespace = invoker.root_namespace + + compiler_root = "//tools/json_schema_compiler" + compiler_script = "$compiler_root/compiler.py" + compiler_sources = [ + "$compiler_root/cc_generator.py", + "$compiler_root/code.py", + "$compiler_root/compiler.py", + "$compiler_root/cpp_generator.py", + "$compiler_root/cpp_type_generator.py", + "$compiler_root/cpp_util.py", + "$compiler_root/h_generator.py", + "$compiler_root/idl_schema.py", + "$compiler_root/model.py", + "$compiler_root/util_cc_helper.py", + ] + + if (schemas) { + schema_generator_name = target_name + "_schema_generator" + action_foreach(schema_generator_name) { + script = compiler_script + sources = invoker.sources + inputs = compiler_sources + outputs = [ + "$target_gen_dir/{{source_name_part}}.cc", + "$target_gen_dir/{{source_name_part}}.h", + ] + args = [ + "{{source}}", + "--root=" + rebase_path("//", root_build_dir), + "--destdir=" + rebase_path(root_gen_dir, root_build_dir), + "--namespace=$root_namespace", + "--generator=cpp", + "--include-rules=$schema_include_rules", + ] + + if (defined(invoker.visibility)) { + # If visibility is restricted, add our own target to it. + visibility = invoker.visibility + target_visibility + } + } + } + + if (bundle) { + uncompiled_sources = [] + if (defined(invoker.uncompiled_sources)) { + uncompiled_sources = invoker.uncompiled_sources + } + + bundle_generator_schema_name = target_name + "_bundle_generator_schema" + action(bundle_generator_schema_name) { + script = compiler_script + inputs = compiler_sources + invoker.sources + uncompiled_sources + outputs = [ + "$target_gen_dir/generated_schemas.cc", + "$target_gen_dir/generated_schemas.h", + ] + args = [ + "--root=" + rebase_path("//", root_build_dir), + "--destdir=" + rebase_path(root_gen_dir, root_build_dir), + "--namespace=$root_namespace", + "--generator=cpp-bundle-schema", + "--include-rules=$schema_include_rules", + ] + rebase_path(invoker.sources, root_build_dir) + + rebase_path(uncompiled_sources, root_build_dir) + } + } + + if (bundle_registration) { + uncompiled_sources = [] + if (defined(invoker.uncompiled_sources)) { + uncompiled_sources = invoker.uncompiled_sources + } + + assert(defined(invoker.impl_dir), + "\"impl_dir\" must be defined for the $target_name template.") + + # Child directory inside the generated file tree. + gen_child_dir = rebase_path(invoker.impl_dir, "//") + + bundle_generator_registration_name = + target_name + "_bundle_generator_registration" + action(bundle_generator_registration_name) { + script = compiler_script + inputs = compiler_sources + invoker.sources + uncompiled_sources + outputs = [ + "$root_gen_dir/$gen_child_dir/generated_api_registration.cc", + "$root_gen_dir/$gen_child_dir/generated_api_registration.h", + ] + args = [ + "--root=" + rebase_path("//", root_build_dir), + "--destdir=" + rebase_path(root_gen_dir, root_build_dir), + "--namespace=$root_namespace", + "--generator=cpp-bundle-registration", + "--impl-dir=$gen_child_dir", + "--include-rules=$schema_include_rules", + ] + rebase_path(invoker.sources, root_build_dir) + + rebase_path(uncompiled_sources, root_build_dir) + } + } + + # Compute the contents of the library/source set. + lib_sources = invoker.sources + lib_deps = [] + lib_public_deps = [] + lib_extra_configs = [] + + if (schemas) { + lib_sources += get_target_outputs(":$schema_generator_name") + lib_public_deps += [ ":$schema_generator_name" ] + lib_deps += [ "//tools/json_schema_compiler:generated_api_util" ] + lib_extra_configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] + } + + if (bundle) { + lib_sources += get_target_outputs(":$bundle_generator_schema_name") + lib_deps += [ ":$bundle_generator_schema_name" ] + } + + if (bundle_registration) { + lib_sources += get_target_outputs(":$bundle_generator_registration_name") + lib_deps += [ ":$bundle_generator_registration_name" ] + } + + if (defined(invoker.deps)) { + lib_deps += invoker.deps + } + + # Generate either a static library or a source set. + if (defined(invoker.generate_static_library) && + invoker.generate_static_library) { + static_library(target_name) { + sources = lib_sources + deps = lib_deps + public_deps = lib_public_deps + configs += lib_extra_configs + public_configs = [ ":$generated_config_name" ] + + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + if (defined(invoker.output_name)) { + output_name = invoker.output_name + } + } + } else { + source_set(target_name) { + sources = lib_sources + deps = lib_deps + public_deps = lib_public_deps + configs += lib_extra_configs + public_configs = [ ":$generated_config_name" ] + + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + if (defined(invoker.output_name)) { + output_name = invoker.output_name + } + } + } +} diff --git a/engine/src/flutter/build/landmine_utils.py b/engine/src/flutter/build/landmine_utils.py new file mode 100644 index 0000000000..6d18b6d0f3 --- /dev/null +++ b/engine/src/flutter/build/landmine_utils.py @@ -0,0 +1,120 @@ +# Copyright 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. + + +import functools +import logging +import os +import shlex +import sys + + +def memoize(default=None): + """This decorator caches the return value of a parameterless pure function""" + def memoizer(func): + val = [] + @functools.wraps(func) + def inner(): + if not val: + ret = func() + val.append(ret if ret is not None else default) + if logging.getLogger().isEnabledFor(logging.INFO): + print '%s -> %r' % (func.__name__, val[0]) + return val[0] + return inner + return memoizer + + +@memoize() +def IsWindows(): + return sys.platform in ['win32', 'cygwin'] + + +@memoize() +def IsLinux(): + return sys.platform.startswith(('linux', 'freebsd', 'openbsd')) + + +@memoize() +def IsMac(): + return sys.platform == 'darwin' + + +@memoize() +def gyp_defines(): + """Parses and returns GYP_DEFINES env var as a dictionary.""" + return dict(arg.split('=', 1) + for arg in shlex.split(os.environ.get('GYP_DEFINES', ''))) + +@memoize() +def gyp_generator_flags(): + """Parses and returns GYP_GENERATOR_FLAGS env var as a dictionary.""" + return dict(arg.split('=', 1) + for arg in shlex.split(os.environ.get('GYP_GENERATOR_FLAGS', ''))) + +@memoize() +def gyp_msvs_version(): + return os.environ.get('GYP_MSVS_VERSION', '') + +@memoize() +def distributor(): + """ + Returns a string which is the distributed build engine in use (if any). + Possible values: 'goma', 'ib', '' + """ + if 'goma' in gyp_defines(): + return 'goma' + elif IsWindows(): + if 'CHROME_HEADLESS' in os.environ: + return 'ib' # use (win and !goma and headless) as approximation of ib + + +@memoize() +def platform(): + """ + Returns a string representing the platform this build is targetted for. + Possible values: 'win', 'mac', 'linux', 'ios', 'android' + """ + if 'OS' in gyp_defines(): + if 'android' in gyp_defines()['OS']: + return 'android' + else: + return gyp_defines()['OS'] + elif IsWindows(): + return 'win' + elif IsLinux(): + return 'linux' + else: + return 'mac' + + +@memoize() +def builder(): + """ + Returns a string representing the build engine (not compiler) to use. + Possible values: 'make', 'ninja', 'xcode', 'msvs', 'scons' + """ + if 'GYP_GENERATORS' in os.environ: + # for simplicity, only support the first explicit generator + generator = os.environ['GYP_GENERATORS'].split(',')[0] + if generator.endswith('-android'): + return generator.split('-')[0] + elif generator.endswith('-ninja'): + return 'ninja' + else: + return generator + else: + if platform() == 'android': + # Good enough for now? Do any android bots use make? + return 'ninja' + elif platform() == 'ios': + return 'xcode' + elif IsWindows(): + return 'ninja' + elif IsLinux(): + return 'ninja' + elif IsMac(): + return 'ninja' + else: + assert False, 'Don\'t know what builder we\'re using!' diff --git a/engine/src/flutter/build/landmines.py b/engine/src/flutter/build/landmines.py new file mode 100755 index 0000000000..bac29823c3 --- /dev/null +++ b/engine/src/flutter/build/landmines.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +""" +This script runs every build as the first hook (See DEPS). If it detects that +the build should be clobbered, it will delete the contents of the build +directory. + +A landmine is tripped when a builder checks out a different revision, and the +diff between the new landmines and the old ones is non-null. At this point, the +build is clobbered. +""" + +import difflib +import errno +import logging +import optparse +import os +import shutil +import sys +import subprocess +import time + +import landmine_utils + + +SRC_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + + +def get_build_dir(build_tool, is_iphone=False): + """ + Returns output directory absolute path dependent on build and targets. + Examples: + r'c:\b\build\slave\win\build\src\out' + '/mnt/data/b/build/slave/linux/build/src/out' + '/b/build/slave/ios_rel_device/build/src/xcodebuild' + + Keep this function in sync with tools/build/scripts/slave/compile.py + """ + ret = None + if build_tool == 'xcode': + ret = os.path.join(SRC_DIR, 'xcodebuild') + elif build_tool in ['make', 'ninja', 'ninja-ios']: # TODO: Remove ninja-ios. + if 'CHROMIUM_OUT_DIR' in os.environ: + output_dir = os.environ.get('CHROMIUM_OUT_DIR').strip() + if not output_dir: + raise Error('CHROMIUM_OUT_DIR environment variable is set but blank!') + else: + output_dir = landmine_utils.gyp_generator_flags().get('output_dir', 'out') + ret = os.path.join(SRC_DIR, output_dir) + else: + raise NotImplementedError('Unexpected GYP_GENERATORS (%s)' % build_tool) + return os.path.abspath(ret) + + +def extract_gn_build_commands(build_ninja_file): + """Extracts from a build.ninja the commands to run GN. + + The commands to run GN are the gn rule and build.ninja build step at the + top of the build.ninja file. We want to keep these when deleting GN builds + since we want to preserve the command-line flags to GN. + + On error, returns the empty string.""" + result = "" + with open(build_ninja_file, 'r') as f: + # Read until the second blank line. The first thing GN writes to the file + # is the "rule gn" and the second is the section for "build build.ninja", + # separated by blank lines. + num_blank_lines = 0 + while num_blank_lines < 2: + line = f.readline() + if len(line) == 0: + return '' # Unexpected EOF. + result += line + if line[0] == '\n': + num_blank_lines = num_blank_lines + 1 + return result + +def delete_build_dir(build_dir): + # GN writes a build.ninja.d file. Note that not all GN builds have args.gn. + build_ninja_d_file = os.path.join(build_dir, 'build.ninja.d') + if not os.path.exists(build_ninja_d_file): + shutil.rmtree(build_dir) + return + + # GN builds aren't automatically regenerated when you sync. To avoid + # messing with the GN workflow, erase everything but the args file, and + # write a dummy build.ninja file that will automatically rerun GN the next + # time Ninja is run. + build_ninja_file = os.path.join(build_dir, 'build.ninja') + build_commands = extract_gn_build_commands(build_ninja_file) + + try: + gn_args_file = os.path.join(build_dir, 'args.gn') + with open(gn_args_file, 'r') as f: + args_contents = f.read() + except IOError: + args_contents = '' + + shutil.rmtree(build_dir) + + # Put back the args file (if any). + os.mkdir(build_dir) + if args_contents != '': + with open(gn_args_file, 'w') as f: + f.write(args_contents) + + # Write the build.ninja file sufficiently to regenerate itself. + with open(os.path.join(build_dir, 'build.ninja'), 'w') as f: + if build_commands != '': + f.write(build_commands) + else: + # Couldn't parse the build.ninja file, write a default thing. + f.write('''rule gn +command = gn -q gen //out/%s/ +description = Regenerating ninja files + +build build.ninja: gn +generator = 1 +depfile = build.ninja.d +''' % (os.path.split(build_dir)[1])) + + # Write a .d file for the build which references a nonexistant file. This + # will make Ninja always mark the build as dirty. + with open(build_ninja_d_file, 'w') as f: + f.write('build.ninja: nonexistant_file.gn\n') + + +def clobber_if_necessary(new_landmines): + """Does the work of setting, planting, and triggering landmines.""" + out_dir = get_build_dir(landmine_utils.builder()) + landmines_path = os.path.normpath(os.path.join(out_dir, '..', '.landmines')) + try: + os.makedirs(out_dir) + except OSError as e: + if e.errno == errno.EEXIST: + pass + + if os.path.exists(landmines_path): + with open(landmines_path, 'r') as f: + old_landmines = f.readlines() + if old_landmines != new_landmines: + old_date = time.ctime(os.stat(landmines_path).st_ctime) + diff = difflib.unified_diff(old_landmines, new_landmines, + fromfile='old_landmines', tofile='new_landmines', + fromfiledate=old_date, tofiledate=time.ctime(), n=0) + sys.stdout.write('Clobbering due to:\n') + sys.stdout.writelines(diff) + + # Clobber contents of build directory but not directory itself: some + # checkouts have the build directory mounted. + for f in os.listdir(out_dir): + path = os.path.join(out_dir, f) + if os.path.isfile(path): + os.unlink(path) + elif os.path.isdir(path): + delete_build_dir(path) + + # Save current set of landmines for next time. + with open(landmines_path, 'w') as f: + f.writelines(new_landmines) + + +def process_options(): + """Returns a list of landmine emitting scripts.""" + parser = optparse.OptionParser() + parser.add_option( + '-s', '--landmine-scripts', action='append', + default=[os.path.join(SRC_DIR, 'build', 'get_landmines.py')], + help='Path to the script which emits landmines to stdout. The target ' + 'is passed to this script via option -t. Note that an extra ' + 'script can be specified via an env var EXTRA_LANDMINES_SCRIPT.') + parser.add_option('-v', '--verbose', action='store_true', + default=('LANDMINES_VERBOSE' in os.environ), + help=('Emit some extra debugging information (default off). This option ' + 'is also enabled by the presence of a LANDMINES_VERBOSE environment ' + 'variable.')) + + options, args = parser.parse_args() + + if args: + parser.error('Unknown arguments %s' % args) + + logging.basicConfig( + level=logging.DEBUG if options.verbose else logging.ERROR) + + extra_script = os.environ.get('EXTRA_LANDMINES_SCRIPT') + if extra_script: + return options.landmine_scripts + [extra_script] + else: + return options.landmine_scripts + + +def main(): + landmine_scripts = process_options() + + if landmine_utils.builder() in ('dump_dependency_json', 'eclipse'): + return 0 + + + landmines = [] + for s in landmine_scripts: + proc = subprocess.Popen([sys.executable, s], stdout=subprocess.PIPE) + output, _ = proc.communicate() + landmines.extend([('%s\n' % l.strip()) for l in output.splitlines()]) + clobber_if_necessary(landmines) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/ls.py b/engine/src/flutter/build/ls.py new file mode 100755 index 0000000000..638c3bdeb7 --- /dev/null +++ b/engine/src/flutter/build/ls.py @@ -0,0 +1,31 @@ +#!/usr/bin/python +# Copyright 2015 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. + +"""Recursively list files of the target directory. Ignores dot files.""" + +import argparse +import os +import sys + +def main(target_directory): + for root, dirs, files in os.walk(target_directory): + files = [f for f in files if not f[0] == '.'] + dirs[:] = [d for d in dirs if not d[0] == '.'] + for f in files: + path = os.path.join(root, f) + print path + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description="Recursively list files of the target directory") + parser.add_argument("--target-directory", + dest="target_directory", + metavar="", + type=str, + required=True, + help="The target directory") + + args = parser.parse_args() + sys.exit(main(args.target_directory)) diff --git a/engine/src/flutter/build/module_args/mojo.gni b/engine/src/flutter/build/module_args/mojo.gni new file mode 100644 index 0000000000..d7cf153c60 --- /dev/null +++ b/engine/src/flutter/build/module_args/mojo.gni @@ -0,0 +1,16 @@ +# Copyright 2015 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. + +# This variable should point to the parent directory of the Mojo SDK. +mojo_sdk_root = "//" + +# To build the Mojo shell from source, set this variable to true. To use the +# prebuilt shell, omit this variable or set it to false. Note that the prebuilt +# shell will be used only on platforms for which it is published (currently +# Linux and Android). +mojo_build_mojo_shell_from_source = true + +# To build the network service from source, set this variable to true. To use +# the prebuilt network service, omit this variable or set it to false. +mojo_build_network_service_from_source = false diff --git a/engine/src/flutter/build/module_args/v8.gni b/engine/src/flutter/build/module_args/v8.gni new file mode 100644 index 0000000000..aa2848c40b --- /dev/null +++ b/engine/src/flutter/build/module_args/v8.gni @@ -0,0 +1,13 @@ +# Copyright 2014 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. + +if (is_android) { + import("//build/config/android/config.gni") +} + +# TODO(sky): nuke this. Temporary while sorting out http://crbug.com/465456. +enable_correct_v8_arch = false + +v8_use_external_startup_data = false +v8_extra_library_files = [] diff --git a/engine/src/flutter/build/output_dll_copy.rules b/engine/src/flutter/build/output_dll_copy.rules new file mode 100644 index 0000000000..c6e905131d --- /dev/null +++ b/engine/src/flutter/build/output_dll_copy.rules @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/engine/src/flutter/build/precompile.cc b/engine/src/flutter/build/precompile.cc new file mode 100644 index 0000000000..db1ef6dfe5 --- /dev/null +++ b/engine/src/flutter/build/precompile.cc @@ -0,0 +1,7 @@ +// Copyright (c) 2011 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. + +// Precompiled header generator for Windows builds. No include is needed +// in this file as the PCH include is forced via the "Forced Include File" +// flag in the projects generated by GYP. diff --git a/engine/src/flutter/build/precompile.h b/engine/src/flutter/build/precompile.h new file mode 100644 index 0000000000..32c2f11059 --- /dev/null +++ b/engine/src/flutter/build/precompile.h @@ -0,0 +1,109 @@ +// Copyright (c) 2012 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. + +// Precompiled header for Chromium project on Windows, not used by +// other build configurations. Using precompiled headers speeds the +// build up significantly, around 1/4th on VS 2010 on an HP Z600 with 12 +// GB of memory. +// +// Numeric comments beside includes are the number of times they were +// included under src/chrome/browser on 2011/8/20, which was used as a +// baseline for deciding what to include in the PCH. Includes without +// a numeric comment are generally included at least 5 times. It may +// be possible to tweak the speed of the build by commenting out or +// removing some of the less frequently used headers. + +#if defined(BUILD_PRECOMPILE_H_) +#error You shouldn't include the precompiled header file more than once. +#endif + +#define BUILD_PRECOMPILE_H_ + +#define _USE_MATH_DEFINES + +// The Windows header needs to come before almost all the other +// Windows-specific headers. +#include +#include +#include +#include // 2 + +// Defines in atlbase.h cause conflicts; if we could figure out how +// this family of headers can be included in the PCH, it might speed +// up the build as several of them are used frequently. +/* +#include +#include +#include +#include // 2 +#include // 2 +#include // 2 +#include // 1 +#include // 1 +#include // 2 +*/ + +// Objbase.h and other files that rely on it bring in [ #define +// interface struct ] which can cause problems in a multi-platform +// build like Chrome's. #undef-ing it does not work as there are +// currently 118 targets that break if we do this, so leaving out of +// the precompiled header for now. +//#include // 2 +//#include // 3 +//#include // 2 +//#include // 2 +//#include // 1 +//#include // 1 +//#include // 2 +//#include // 1 +//#include // 1 +//#include // 2 +//#include // 2 +//#include // 2 +//#include // 1 +//#include // 1 +//#include // 4 +//#include // 2 + +// Caused other conflicts in addition to the 'interface' issue above. +// #include + +#include +#include +#include // 4 +#include +#include // 1 +#include +#include // 1 +#include +#include +#include +#include +#include // 4 + +#include +#include // 3 +#include +#include +#include // 3 +#include // 2 +#include +#include +#include // 3 +#include +#include // 2 +#include // 2 +#include +#include +#include +#include +#include // 2 +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/engine/src/flutter/build/protoc_java.py b/engine/src/flutter/build/protoc_java.py new file mode 100755 index 0000000000..470667c1f4 --- /dev/null +++ b/engine/src/flutter/build/protoc_java.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Generate java source files from protobuf files. + +This is a helper file for the genproto_java action in protoc_java.gypi. + +It performs the following steps: +1. Deletes all old sources (ensures deleted classes are not part of new jars). +2. Creates source directory. +3. Generates Java files using protoc (output into either --java-out-dir or + --srcjar). +4. Creates a new stamp file. +""" + +import os +import optparse +import shutil +import subprocess +import sys + +sys.path.append(os.path.join(os.path.dirname(__file__), "android", "gyp")) +from util import build_utils + +def main(argv): + parser = optparse.OptionParser() + build_utils.AddDepfileOption(parser) + parser.add_option("--protoc", help="Path to protoc binary.") + parser.add_option("--proto-path", help="Path to proto directory.") + parser.add_option("--java-out-dir", + help="Path to output directory for java files.") + parser.add_option("--srcjar", help="Path to output srcjar.") + parser.add_option("--stamp", help="File to touch on success.") + options, args = parser.parse_args(argv) + + build_utils.CheckOptions(options, parser, ['protoc', 'proto_path']) + if not options.java_out_dir and not options.srcjar: + print 'One of --java-out-dir or --srcjar must be specified.' + return 1 + + with build_utils.TempDir() as temp_dir: + # Specify arguments to the generator. + generator_args = ['optional_field_style=reftypes', + 'store_unknown_fields=true'] + out_arg = '--javanano_out=' + ','.join(generator_args) + ':' + temp_dir + # Generate Java files using protoc. + build_utils.CheckOutput( + [options.protoc, '--proto_path', options.proto_path, out_arg] + + args) + + if options.java_out_dir: + build_utils.DeleteDirectory(options.java_out_dir) + shutil.copytree(temp_dir, options.java_out_dir) + else: + build_utils.ZipDir(options.srcjar, temp_dir) + + if options.depfile: + build_utils.WriteDepfile( + options.depfile, + args + [options.protoc] + build_utils.GetPythonDependencies()) + + if options.stamp: + build_utils.Touch(options.stamp) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/build/rmdir_and_stamp.py b/engine/src/flutter/build/rmdir_and_stamp.py new file mode 100755 index 0000000000..6aa11f85fd --- /dev/null +++ b/engine/src/flutter/build/rmdir_and_stamp.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Wipes out a directory recursively and then touches a stamp file. + +This odd pairing of operations is used to support build scripts which +slurp up entire directories (e.g. build/android/javac.py when handling +generated sources) as inputs. + +The general pattern of use is: + + - Add a target which generates |gen_sources| into |out_path| from |inputs|. + - Include |stamp_file| as an input for that target or any of its rules which + generate files in |out_path|. + - Add an action which depends on |inputs| and which outputs |stamp_file|; + the action should run this script and pass |out_path| and |stamp_file| as + its arguments. + +The net result is that you will force |out_path| to be wiped and all +|gen_sources| to be regenerated any time any file in |inputs| changes. + +See //third_party/mojo/mojom_bindings_generator.gypi for an example use case. + +""" + +import errno +import os +import shutil +import sys + + +def Main(dst_dir, stamp_file): + try: + shutil.rmtree(os.path.normpath(dst_dir)) + except OSError as e: + # Ignore only "not found" errors. + if e.errno != errno.ENOENT: + raise e + with open(stamp_file, 'a'): + os.utime(stamp_file, None) + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1], sys.argv[2])) diff --git a/engine/src/flutter/build/sanitize-mac-build-log.sed b/engine/src/flutter/build/sanitize-mac-build-log.sed new file mode 100644 index 0000000000..b4111c7b82 --- /dev/null +++ b/engine/src/flutter/build/sanitize-mac-build-log.sed @@ -0,0 +1,33 @@ +# Copyright (c) 2012 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. + +# Use this sed script to reduce a Mac build log into something readable. + +# Drop uninformative lines. +/^distcc/d +/^Check dependencies/d +/^ setenv /d +/^ cd /d +/^make: Nothing to be done/d +/^$/d + +# Xcode prints a short "compiling foobar.o" line followed by the lengthy +# full command line. These deletions drop the command line. +\|^ /Developer/usr/bin/|d +\|^ /Developer/Library/PrivateFrameworks/DevToolsCore\.framework/|d +\|^ /Developer/Library/Xcode/Plug-ins/CoreBuildTasks\.xcplugin/|d + +# Drop any goma command lines as well. +\|^ .*/gomacc |d + +# And, if you've overridden something from your own bin directory, remove those +# full command lines, too. +\|^ /Users/[^/]*/bin/|d + +# There's already a nice note for bindings, don't need the command line. +\|^python scripts/rule_binding\.py|d + +# Shorten the "compiling foobar.o" line. +s|^Distributed-CompileC (.*) normal i386 c\+\+ com\.apple\.compilers\.gcc\.4_2| CC \1| +s|^CompileC (.*) normal i386 c\+\+ com\.apple\.compilers\.gcc\.4_2| CC \1| diff --git a/engine/src/flutter/build/sanitize-mac-build-log.sh b/engine/src/flutter/build/sanitize-mac-build-log.sh new file mode 100755 index 0000000000..df5a7af29e --- /dev/null +++ b/engine/src/flutter/build/sanitize-mac-build-log.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# Copyright (c) 2010 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. +sed -r -f `dirname "${0}"`/`basename "${0}" sh`sed diff --git a/engine/src/flutter/build/sanitize-win-build-log.sed b/engine/src/flutter/build/sanitize-win-build-log.sed new file mode 100644 index 0000000000..c18e664c83 --- /dev/null +++ b/engine/src/flutter/build/sanitize-win-build-log.sed @@ -0,0 +1,15 @@ +# Copyright (c) 2012 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. + +# Use this sed script to reduce a Windows build log into something +# machine-parsable. + +# Drop uninformative lines. +/The operation completed successfully\./d + +# Drop parallelization indicators on lines. +s/^[0-9]+>// + +# Shorten bindings generation lines +s/^.*"python".*idl_compiler\.py".*("[^"]+\.idl").*$/ idl_compiler \1/ diff --git a/engine/src/flutter/build/sanitize-win-build-log.sh b/engine/src/flutter/build/sanitize-win-build-log.sh new file mode 100755 index 0000000000..df5a7af29e --- /dev/null +++ b/engine/src/flutter/build/sanitize-win-build-log.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# Copyright (c) 2010 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. +sed -r -f `dirname "${0}"`/`basename "${0}" sh`sed diff --git a/engine/src/flutter/build/sanitizers/BUILD.gn b/engine/src/flutter/build/sanitizers/BUILD.gn new file mode 100644 index 0000000000..4f81f3ebad --- /dev/null +++ b/engine/src/flutter/build/sanitizers/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright (c) 2015 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. + +if (is_linux && !is_chromeos) { + # TODO(GYP): Figure out which of these work and are needed on other platforms. + copy("copy_llvm_symbolizer") { + if (is_win) { + sources = [ + "//third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer.exe", + ] + outputs = [ + "$root_out_dir/llvm-symbolizer.exe", + ] + } else { + sources = [ + "//third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer", + ] + outputs = [ + "$root_out_dir/llvm-symbolizer", + ] + } + } +} diff --git a/engine/src/flutter/build/sanitizers/OWNERS b/engine/src/flutter/build/sanitizers/OWNERS new file mode 100644 index 0000000000..0be2be890a --- /dev/null +++ b/engine/src/flutter/build/sanitizers/OWNERS @@ -0,0 +1,4 @@ +glider@chromium.org +earthdok@chromium.org +per-file tsan_suppressions.cc=* +per-file lsan_suppressions.cc=* diff --git a/engine/src/flutter/build/sanitizers/asan_suppressions.cc b/engine/src/flutter/build/sanitizers/asan_suppressions.cc new file mode 100644 index 0000000000..df94bc8950 --- /dev/null +++ b/engine/src/flutter/build/sanitizers/asan_suppressions.cc @@ -0,0 +1,23 @@ +// Copyright 2015 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. + +// This file contains the default suppressions for AddressSanitizer. +// It should only be used under very limited circumstances such as suppressing +// a report caused by an interceptor call in a system-installed library. + +#if defined(ADDRESS_SANITIZER) + +// Please make sure the code below declares a single string variable +// kASanDefaultSuppressions which contains ASan suppressions delimited by +// newlines. +char kASanDefaultSuppressions[] = +// http://crbug.com/178677 +"interceptor_via_lib:libsqlite3.so\n" + +// PLEASE READ ABOVE BEFORE ADDING NEW SUPPRESSIONS. + +// End of suppressions. +; // Please keep this semicolon. + +#endif // ADDRESS_SANITIZER diff --git a/engine/src/flutter/build/sanitizers/lsan_suppressions.cc b/engine/src/flutter/build/sanitizers/lsan_suppressions.cc new file mode 100644 index 0000000000..ac2defa11b --- /dev/null +++ b/engine/src/flutter/build/sanitizers/lsan_suppressions.cc @@ -0,0 +1,105 @@ +// Copyright 2015 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. + +// This file contains the default suppressions for LeakSanitizer. +// You can also pass additional suppressions via LSAN_OPTIONS: +// LSAN_OPTIONS=suppressions=/path/to/suppressions. Please refer to +// http://dev.chromium.org/developers/testing/leaksanitizer for more info. + +#if defined(LEAK_SANITIZER) + +// Please make sure the code below declares a single string variable +// kLSanDefaultSuppressions which contains LSan suppressions delimited by +// newlines. See http://dev.chromium.org/developers/testing/leaksanitizer +// for the instructions on writing suppressions. +char kLSanDefaultSuppressions[] = +// Intentional leak used as sanity test for Valgrind/memcheck. +"leak:base::ToolsSanityTest_MemoryLeak_Test::TestBody\n" + +// ================ Leaks in third-party code ================ + +// False positives in libfontconfig. http://crbug.com/39050 +"leak:libfontconfig\n" + +// Leaks in Nvidia's libGL. +"leak:libGL.so\n" + +// A small leak in V8. http://crbug.com/46571#c9 +"leak:blink::V8GCController::collectGarbage\n" + +// TODO(earthdok): revisit NSS suppressions after the switch to BoringSSL +// NSS leaks in CertDatabaseNSSTest tests. http://crbug.com/51988 +"leak:net::NSSCertDatabase::ImportFromPKCS12\n" +"leak:net::NSSCertDatabase::ListCerts\n" +"leak:net::NSSCertDatabase::DeleteCertAndKey\n" +"leak:crypto::ScopedTestNSSDB::ScopedTestNSSDB\n" +// Another leak due to not shutting down NSS properly. http://crbug.com/124445 +"leak:error_get_my_stack\n" +// The NSS suppressions above will not fire when the fast stack unwinder is +// used, because it can't unwind through NSS libraries. Apply blanket +// suppressions for now. +"leak:libnssutil3\n" +"leak:libnspr4\n" +"leak:libnss3\n" +"leak:libplds4\n" +"leak:libnssckbi\n" + +// XRandR has several one time leaks. +"leak:libxrandr\n" + +// xrandr leak. http://crbug.com/119677 +"leak:XRRFindDisplay\n" + +// Suppressions for objects which can be owned by the V8 heap. This is a +// temporary workaround until LeakSanitizer supports the V8 heap. +// Those should only fire in (browser)tests. If you see one of them in Chrome, +// then it's a real leak. +// http://crbug.com/328552 +"leak:WTF::StringImpl::createUninitialized\n" +"leak:WTF::StringImpl::create8BitIfPossible\n" +"leak:blink::MouseEvent::create\n" +"leak:blink::*::*GetterCallback\n" +"leak:blink::CSSComputedStyleDeclaration::create\n" +"leak:blink::V8PerIsolateData::ensureDomInJSContext\n" +"leak:gin/object_template_builder.h\n" +"leak:gin::internal::Dispatcher\n" +"leak:blink::LocalDOMWindow::getComputedStyle\n" +// This should really be RemoteDOMWindow::create, but symbolization is +// weird in release builds. https://crbug.com/484760 +"leak:blink::RemoteFrame::create\n" +// Likewise, this should really be blink::WindowProxy::initializeIfNeeded. +// https://crbug.com/484760 +"leak:blink::WindowProxy::createContext\n" + +// http://crbug.com/356785 +"leak:content::RenderViewImplTest_DecideNavigationPolicyForWebUI_Test::TestBody\n" + +// ================ Leaks in Chromium code ================ +// PLEASE DO NOT ADD SUPPRESSIONS FOR NEW LEAKS. +// Instead, commits that introduce memory leaks should be reverted. Suppressing +// the leak is acceptable in some cases when reverting is impossible, i.e. when +// enabling leak detection for the first time for a test target with +// pre-existing leaks. + +// Small test-only leak in ppapi_unittests. http://crbug.com/258113 +"leak:ppapi::proxy::PPP_Instance_Private_ProxyTest_PPPInstancePrivate_Test\n" + +// http://crbug.com/322671 +"leak:content::SpeechRecognitionBrowserTest::SetUpOnMainThread\n" + +// http://crbug.com/355641 +"leak:TrayAccessibilityTest\n" + +// http://crbug.com/354644 +"leak:CertificateViewerUITest::ShowModalCertificateViewer\n" + +// http://crbug.com/356306 +"leak:content::SetProcessTitleFromCommandLine\n" + +// PLEASE READ ABOVE BEFORE ADDING NEW SUPPRESSIONS. + +// End of suppressions. +; // Please keep this semicolon. + +#endif // LEAK_SANITIZER diff --git a/engine/src/flutter/build/sanitizers/sanitizer_options.cc b/engine/src/flutter/build/sanitizers/sanitizer_options.cc new file mode 100644 index 0000000000..a659a22caa --- /dev/null +++ b/engine/src/flutter/build/sanitizers/sanitizer_options.cc @@ -0,0 +1,164 @@ +// Copyright 2014 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. +// +// This file contains the default options for various compiler-based dynamic +// tools. + +#include "build/build_config.h" + +#if defined(ADDRESS_SANITIZER) && defined(OS_MACOSX) +#include // for _NSGetArgc, _NSGetArgv +#include +#endif // ADDRESS_SANITIZER && OS_MACOSX + +#if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ + defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) +// Functions returning default options are declared weak in the tools' runtime +// libraries. To make the linker pick the strong replacements for those +// functions from this module, we explicitly force its inclusion by passing +// -Wl,-u_sanitizer_options_link_helper +extern "C" +void _sanitizer_options_link_helper() { } + +// The callbacks we define here will be called from the sanitizer runtime, but +// aren't referenced from the Chrome executable. We must ensure that those +// callbacks are not sanitizer-instrumented, and that they aren't stripped by +// the linker. +#define SANITIZER_HOOK_ATTRIBUTE \ + extern "C" \ + __attribute__((no_sanitize_address)) \ + __attribute__((no_sanitize_memory)) \ + __attribute__((no_sanitize_thread)) \ + __attribute__((visibility("default"))) \ + __attribute__((used)) +#endif + +#if defined(ADDRESS_SANITIZER) +// Default options for AddressSanitizer in various configurations: +// malloc_context_size=5 - limit the size of stack traces collected by ASan +// for each malloc/free by 5 frames. These stack traces tend to accumulate +// very fast in applications using JIT (v8 in Chrome's case), see +// https://code.google.com/p/address-sanitizer/issues/detail?id=177 +// symbolize=false - disable the in-process symbolization, which isn't 100% +// compatible with the existing sandboxes and doesn't make much sense for +// stripped official binaries. +// legacy_pthread_cond=1 - run in the libpthread 2.2.5 compatibility mode to +// work around libGL.so using the obsolete API, see +// http://crbug.com/341805. This may break if pthread_cond_t objects are +// accessed by both instrumented and non-instrumented binaries (e.g. if +// they reside in shared memory). This option is going to be deprecated in +// upstream AddressSanitizer and must not be used anywhere except the +// official builds. +// check_printf=1 - check the memory accesses to printf (and other formatted +// output routines) arguments. +// use_sigaltstack=1 - handle signals on an alternate signal stack. Useful +// for stack overflow detection. +// strip_path_prefix=Release/../../ - prefixes up to and including this +// substring will be stripped from source file paths in symbolized reports +// (if symbolize=true, which is set when running with LeakSanitizer). +// fast_unwind_on_fatal=1 - use the fast (frame-pointer-based) stack unwinder +// to print error reports. V8 doesn't generate debug info for the JIT code, +// so the slow unwinder may not work properly. +// detect_stack_use_after_return=1 - use fake stack to delay the reuse of +// stack allocations and detect stack-use-after-return errors. +#if defined(OS_LINUX) +#if defined(GOOGLE_CHROME_BUILD) +// Default AddressSanitizer options for the official build. These do not affect +// tests on buildbots (which don't set GOOGLE_CHROME_BUILD) or non-official +// Chromium builds. +const char kAsanDefaultOptions[] = + "legacy_pthread_cond=1 malloc_context_size=5 " + "symbolize=false check_printf=1 use_sigaltstack=1 detect_leaks=0 " + "strip_path_prefix=Release/../../ fast_unwind_on_fatal=1"; +#else +// Default AddressSanitizer options for buildbots and non-official builds. +const char *kAsanDefaultOptions = + "symbolize=false check_printf=1 use_sigaltstack=1 " + "detect_leaks=0 strip_path_prefix=Release/../../ fast_unwind_on_fatal=1 " + "detect_stack_use_after_return=1 "; +#endif // GOOGLE_CHROME_BUILD + +#elif defined(OS_MACOSX) +const char *kAsanDefaultOptions = + "check_printf=1 use_sigaltstack=1 " + "strip_path_prefix=Release/../../ fast_unwind_on_fatal=1 " + "detect_stack_use_after_return=1 detect_odr_violation=0 "; +static const char kNaClDefaultOptions[] = "handle_segv=0"; +static const char kNaClFlag[] = "--type=nacl-loader"; +#endif // OS_LINUX + +#if defined(OS_LINUX) || defined(OS_MACOSX) +SANITIZER_HOOK_ATTRIBUTE const char *__asan_default_options() { +#if defined(OS_MACOSX) + char*** argvp = _NSGetArgv(); + int* argcp = _NSGetArgc(); + if (!argvp || !argcp) return kAsanDefaultOptions; + char** argv = *argvp; + int argc = *argcp; + for (int i = 0; i < argc; ++i) { + if (strcmp(argv[i], kNaClFlag) == 0) { + return kNaClDefaultOptions; + } + } +#endif + return kAsanDefaultOptions; +} + +extern "C" char kASanDefaultSuppressions[]; + +SANITIZER_HOOK_ATTRIBUTE const char *__asan_default_suppressions() { + return kASanDefaultSuppressions; +} +#endif // OS_LINUX || OS_MACOSX +#endif // ADDRESS_SANITIZER + +#if defined(THREAD_SANITIZER) && defined(OS_LINUX) +// Default options for ThreadSanitizer in various configurations: +// detect_deadlocks=1 - enable deadlock (lock inversion) detection. +// second_deadlock_stack=1 - more verbose deadlock reports. +// report_signal_unsafe=0 - do not report async-signal-unsafe functions +// called from signal handlers. +// report_thread_leaks=0 - do not report unjoined threads at the end of +// the program execution. +// print_suppressions=1 - print the list of matched suppressions. +// history_size=7 - make the history buffer proportional to 2^7 (the maximum +// value) to keep more stack traces. +// strip_path_prefix=Release/../../ - prefixes up to and including this +// substring will be stripped from source file paths in symbolized reports. +const char kTsanDefaultOptions[] = + "detect_deadlocks=1 second_deadlock_stack=1 report_signal_unsafe=0 " + "report_thread_leaks=0 print_suppressions=1 history_size=7 " + "strip_path_prefix=Release/../../ "; + +SANITIZER_HOOK_ATTRIBUTE const char *__tsan_default_options() { + return kTsanDefaultOptions; +} + +extern "C" char kTSanDefaultSuppressions[]; + +SANITIZER_HOOK_ATTRIBUTE const char *__tsan_default_suppressions() { + return kTSanDefaultSuppressions; +} + +#endif // THREAD_SANITIZER && OS_LINUX + +#if defined(LEAK_SANITIZER) +// Default options for LeakSanitizer: +// print_suppressions=1 - print the list of matched suppressions. +// strip_path_prefix=Release/../../ - prefixes up to and including this +// substring will be stripped from source file paths in symbolized reports. +const char kLsanDefaultOptions[] = + "print_suppressions=1 strip_path_prefix=Release/../../ "; + +SANITIZER_HOOK_ATTRIBUTE const char *__lsan_default_options() { + return kLsanDefaultOptions; +} + +extern "C" char kLSanDefaultSuppressions[]; + +SANITIZER_HOOK_ATTRIBUTE const char *__lsan_default_suppressions() { + return kLSanDefaultSuppressions; +} + +#endif // LEAK_SANITIZER diff --git a/engine/src/flutter/build/sanitizers/sanitizers.gyp b/engine/src/flutter/build/sanitizers/sanitizers.gyp new file mode 100644 index 0000000000..91dab8a238 --- /dev/null +++ b/engine/src/flutter/build/sanitizers/sanitizers.gyp @@ -0,0 +1,92 @@ +# Copyright 2014 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. + +{ + 'targets': [ + { + 'target_name': 'sanitizer_options', + 'type': 'static_library', + 'toolsets': ['host', 'target'], + 'variables': { + # Every target is going to depend on sanitizer_options, so allow + # this one to depend on itself. + 'prune_self_dependency': 1, + # Do not let 'none' targets depend on this one, they don't need to. + 'link_dependency': 1, + }, + 'sources': [ + 'sanitizer_options.cc', + ], + 'include_dirs': [ + '../..', + ], + # Some targets may want to opt-out from ASan, TSan and MSan and link + # without the corresponding runtime libraries. We drop the libc++ + # dependency and omit the compiler flags to avoid bringing instrumented + # code to those targets. + 'conditions': [ + ['use_custom_libcxx==1', { + 'dependencies!': [ + '../../buildtools/third_party/libc++/libc++.gyp:libcxx_proxy', + ], + }], + ['tsan==1', { + 'sources': [ + 'tsan_suppressions.cc', + ], + }], + ['lsan==1', { + 'sources': [ + 'lsan_suppressions.cc', + ], + }], + ['asan==1', { + 'sources': [ + 'asan_suppressions.cc', + ], + }], + ], + 'cflags/': [ + ['exclude', '-fsanitize='], + ['exclude', '-fsanitize-'], + ], + 'direct_dependent_settings': { + 'ldflags': [ + '-Wl,-u_sanitizer_options_link_helper', + ], + 'target_conditions': [ + ['_type=="executable"', { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-Wl,-u,__sanitizer_options_link_helper', + ], + }, + }], + ], + }, + }, + { + # Copy llvm-symbolizer to the product dir so that LKGR bots can package it. + 'target_name': 'llvm-symbolizer', + 'type': 'none', + 'variables': { + + # Path is relative to this GYP file. + 'llvm_symbolizer_path': + '../../third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer<(EXECUTABLE_SUFFIX)', + }, + 'conditions': [ + ['clang==1', { + 'copies': [{ + 'destination': '<(PRODUCT_DIR)', + 'files': [ + '<(llvm_symbolizer_path)', + ], + }], + }], + ], + }, + ], +} + diff --git a/engine/src/flutter/build/sanitizers/tsan_suppressions.cc b/engine/src/flutter/build/sanitizers/tsan_suppressions.cc new file mode 100644 index 0000000000..ace6075662 --- /dev/null +++ b/engine/src/flutter/build/sanitizers/tsan_suppressions.cc @@ -0,0 +1,319 @@ +// Copyright 2014 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. + +// This file contains the default suppressions for ThreadSanitizer. +// You can also pass additional suppressions via TSAN_OPTIONS: +// TSAN_OPTIONS=suppressions=/path/to/suppressions. Please refer to +// http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2 +// for more info. + +#if defined(THREAD_SANITIZER) + +// Please make sure the code below declares a single string variable +// kTSanDefaultSuppressions contains TSan suppressions delimited by newlines. +// See http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2 +// for the instructions on writing suppressions. +char kTSanDefaultSuppressions[] = +// False positives in libflashplayer.so and libglib.so. Since we don't +// instrument them, we cannot reason about the synchronization in them. +"race:libflashplayer.so\n" +"race:libglib*.so\n" + +// Intentional race in ToolsSanityTest.DataRace in base_unittests. +"race:base/tools_sanity_unittest.cc\n" + +// Data race on WatchdogCounter [test-only]. +"race:base/threading/watchdog_unittest.cc\n" + +// Races in libevent, http://crbug.com/23244. +"race:libevent/event.c\n" + +// http://crbug.com/46840. +"race:base::HistogramSamples::IncreaseSum\n" +"race:base::Histogram::Add\n" +"race:base::HistogramSamples::Add\n" + +// http://crbug.com/84094. +"race:sqlite3StatusSet\n" +"race:pcache1EnforceMaxPage\n" +"race:pcache1AllocPage\n" + +// http://crbug.com/102327. +// Test-only race, won't fix. +"race:tracked_objects::ThreadData::ShutdownSingleThreadedCleanup\n" + +// http://crbug.com/115540 +"race:*GetCurrentThreadIdentifier\n" + +// http://crbug.com/120808 +"race:base/threading/watchdog.cc\n" + +// http://crbug.com/157586 +"race:third_party/libvpx/source/libvpx/vp8/decoder/threading.c\n" + +// http://crbug.com/158718 +"race:third_party/ffmpeg/libavcodec/pthread.c\n" +"race:third_party/ffmpeg/libavcodec/pthread_frame.c\n" +"race:third_party/ffmpeg/libavcodec/vp8.c\n" +"race:third_party/ffmpeg/libavutil/mem.c\n" +"race:*HashFrameForTesting\n" +"race:third_party/ffmpeg/libavcodec/h264pred.c\n" +"race:media::ReleaseData\n" + +// http://crbug.com/158922 +"race:third_party/libvpx/source/libvpx/vp8/encoder/*\n" +"race:third_party/libvpx/source/libvpx/vp9/encoder/*\n" + +// http://crbug.com/189177 +"race:thread_manager\n" +"race:v8::Locker::Initialize\n" + +// http://crbug.com/239359 +"race:media::TestInputCallback::OnData\n" + +// http://crbug.com/244368 +"race:skia::BeginPlatformPaint\n" + +// http://crbug.com/244385 +"race:unixTempFileDir\n" + +// http://crbug.com/244755 +"race:v8::internal::Zone::NewExpand\n" +"race:TooLateToEnableNow\n" +"race:adjust_segment_bytes_allocated\n" + +// http://crbug.com/244774 +"race:webrtc::RTPReceiver::ProcessBitrate\n" +"race:webrtc::RTPSender::ProcessBitrate\n" +"race:webrtc::VideoCodingModuleImpl::Decode\n" +"race:webrtc::RTPSender::SendOutgoingData\n" +"race:webrtc::VP8EncoderImpl::GetEncodedPartitions\n" +"race:webrtc::VP8EncoderImpl::Encode\n" +"race:webrtc::ViEEncoder::DeliverFrame\n" +"race:webrtc::vcm::VideoReceiver::Decode\n" +"race:webrtc::VCMReceiver::FrameForDecoding\n" +"race:*trace_event_unique_catstatic*\n" + +// http://crbug.com/244856 +"race:AutoPulseLock\n" + +// http://crbug.com/246968 +"race:webrtc::VideoCodingModuleImpl::RegisterPacketRequestCallback\n" + +// http://crbug.com/246974 +"race:content::GpuWatchdogThread::CheckArmed\n" + +// http://crbug.com/257396 +"race:base::trace_event::" + "TraceEventTestFixture_TraceSamplingScope_Test::TestBody\n" + +// http://crbug.com/258479 +"race:SamplingStateScope\n" +"race:g_trace_state\n" + +// http://crbug.com/258499 +"race:third_party/skia/include/core/SkRefCnt.h\n" + +// http://crbug.com/268924 +"race:base::g_power_monitor\n" +"race:base::PowerMonitor::PowerMonitor\n" +"race:base::PowerMonitor::AddObserver\n" +"race:base::PowerMonitor::RemoveObserver\n" +"race:base::PowerMonitor::IsOnBatteryPower\n" + +// http://crbug.com/258935 +"race:base::Thread::StopSoon\n" + +// http://crbug.com/268941 +"race:tracked_objects::ThreadData::tls_index_\n" + +// http://crbug.com/272095 +"race:base::g_top_manager\n" + +// http://crbug.com/273047 +"race:base::*::g_lazy_tls_ptr\n" +"race:IPC::SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_\n" + +// http://crbug.com/280466 +"race:content::WebRtcAudioCapturer::SetCapturerSource\n" + +// http://crbug.com/285242 +"race:media::PulseAudioOutputStream::SetVolume\n" + +// http://crbug.com/308590 +"race:CustomThreadWatcher::~CustomThreadWatcher\n" + +// http://crbug.com/310851 +"race:net::ProxyResolverV8Tracing::Job::~Job\n" + +// http://crbug.com/313726 +"race:CallbackWasCalled\n" + +// http://crbug.com/327330 +"race:PrepareTextureMailbox\n" +"race:cc::LayerTreeHost::PaintLayerContents\n" + +// http://crbug.com/476529 +"deadlock:cc::VideoLayerImpl::WillDraw\n" + +// http://crbug.com/328826 +"race:gLCDOrder\n" +"race:gLCDOrientation\n" + +// http://crbug.com/328868 +"race:PR_Lock\n" + +// http://crbug.com/329225 +"race:blink::currentTimeFunction\n" + +// http://crbug.com/329460 +"race:extensions::InfoMap::AddExtension\n" + +// http://crbug.com/333244 +"race:content::" + "VideoCaptureImplTest::MockVideoCaptureImpl::~MockVideoCaptureImpl\n" + +// http://crbug.com/333871 +"race:v8::internal::Interface::NewValue()::value_interface\n" +"race:v8::internal::IsMinusZero(double)::minus_zero\n" +"race:v8::internal::FastCloneShallowObjectStub::InitializeInterfaceDescriptor\n" +"race:v8::internal::KeyedLoadStubCompiler::registers\n" +"race:v8::internal::KeyedStoreStubCompiler::registers()::registers\n" +"race:v8::internal::KeyedLoadFastElementStub::InitializeInterfaceDescriptor\n" +"race:v8::internal::KeyedStoreFastElementStub::InitializeInterfaceDescriptor\n" +"race:v8::internal::LoadStubCompiler::registers\n" +"race:v8::internal::StoreStubCompiler::registers\n" +"race:v8::internal::HValue::LoopWeight\n" + +// http://crbug.com/334140 +"race:CommandLine::HasSwitch\n" +"race:CommandLine::current_process_commandline_\n" +"race:CommandLine::GetSwitchValueASCII\n" + +// http://crbug.com/338675 +"race:blink::s_platform\n" +"race:content::" + "RendererWebKitPlatformSupportImpl::~RendererWebKitPlatformSupportImpl\n" + +// http://crbug.com/345240 +"race:WTF::s_shutdown\n" + +// http://crbug.com/345245 +"race:jingle_glue::JingleThreadWrapper::~JingleThreadWrapper\n" +"race:webrtc::voe::Channel::UpdatePacketDelay\n" +"race:webrtc::voe::Channel::GetDelayEstimate\n" +"race:webrtc::VCMCodecDataBase::DeregisterReceiveCodec\n" +"race:webrtc::GainControlImpl::set_stream_analog_level\n" + +// http://crbug.com/345618 +"race:WebCore::AudioDestinationNode::render\n" + +// http://crbug.com/345624 +"race:media::DataSource::set_host\n" + +// http://crbug.com/347534 +"race:v8::internal::V8::TearDown\n" + +// http://crbug.com/347538 +"race:sctp_timer_start\n" + +// http://crbug.com/347548 +"race:cricket::WebRtcVideoMediaChannel::MaybeResetVieSendCodec\n" +"race:cricket::WebRtcVideoMediaChannel::SetSendCodec\n" + +// http://crbug.com/347553 +"race:blink::WebString::reset\n" + +// http://crbug.com/348511 +"race:webrtc::acm1::AudioCodingModuleImpl::PlayoutData10Ms\n" + +// http://crbug.com/348982 +"race:cricket::P2PTransportChannel::OnConnectionDestroyed\n" +"race:cricket::P2PTransportChannel::AddConnection\n" + +// http://crbug.com/348984 +"race:sctp_express_handle_sack\n" +"race:system_base_info\n" + +// http://crbug.com/363999 +"race:v8::internal::EnterDebugger::*EnterDebugger\n" + +// https://code.google.com/p/v8/issues/detail?id=3143 +"race:v8::internal::FLAG_track_double_fields\n" + +// https://crbug.com/369257 +// TODO(mtklein): annotate properly and remove suppressions. +"race:SandboxIPCHandler::HandleFontMatchRequest\n" +"race:SkFontConfigInterfaceDirect::matchFamilyName\n" +"race:SkFontConfigInterface::GetSingletonDirectInterface\n" +"race:FcStrStaticName\n" + +// http://crbug.com/372807 +"deadlock:net::X509Certificate::CreateCertificateListFromBytes\n" +"deadlock:net::X509Certificate::CreateFromBytes\n" +"deadlock:net::SSLClientSocketNSS::Core::DoHandshakeLoop\n" + +// http://crbug.com/374135 +"race:media::AlsaWrapper::PcmWritei\n" + +// False positive in libc's tzset_internal, http://crbug.com/379738. +"race:tzset_internal\n" + +// http://crbug.com/380554 +"deadlock:g_type_add_interface_static\n" + +// http:://crbug.com/386385 +"race:content::AppCacheStorageImpl::DatabaseTask::CallRunCompleted\n" + +// http://crbug.com/388730 +"race:g_next_user_script_id\n" + +// http://crbug.com/389098 +"race:webrtc::RtpToNtpMs\n" +"race:webrtc::UpdateRtcpList\n" +"race:webrtc::RemoteNtpTimeEstimator::Estimate\n" +"race:webrtc::voe::TransmitMixer::EnableStereoChannelSwapping\n" + +// http://crbug.com/397022 +"deadlock:" +"base::trace_event::TraceEventTestFixture_ThreadOnceBlocking_Test::TestBody\n" + +// http://crbug.com/415472 +"deadlock:base::trace_event::TraceLog::GetCategoryGroupEnabled\n" + +// http://crbug.com/490856 +"deadlock:content::TracingControllerImpl::SetEnabledOnFileThread\n" + +// http://crbug.com/417193 +// Suppressing both AudioContext.{cpp,h}. +"race:modules/webaudio/AudioContext\n" + +// https://code.google.com/p/skia/issues/detail?id=3294 +"race:SkBaseMutex::acquire\n" + +// https://crbug.com/430533 +"race:TileTaskGraphRunner::Run\n" + +// https://crbug.com/448203 +"race:blink::RemoteFrame::detach\n" + +// https://crbug.com/454652 +"race:net::NetworkChangeNotifier::SetTestNotificationsOnly\n" + +// https://crbug.com/455638 +"deadlock:dbus::Bus::ShutdownAndBlock\n" + +// https://crbug.com/455665 +"race:mojo::common::*::tick_clock\n" + +// https://crbug.com/459429 +"race:randomnessPid\n" + +// https://crbug.com/454655 +"race:content::BrowserTestBase::PostTaskToInProcessRendererAndWait\n" + +// End of suppressions. +; // Please keep this semicolon. + +#endif // THREAD_SANITIZER diff --git a/engine/src/flutter/build/secondary/testing/gmock/BUILD.gn b/engine/src/flutter/build/secondary/testing/gmock/BUILD.gn new file mode 100644 index 0000000000..4ec62245e6 --- /dev/null +++ b/engine/src/flutter/build/secondary/testing/gmock/BUILD.gn @@ -0,0 +1,54 @@ +# Copyright 2014 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. + +config("gmock_config") { + # Gmock headers need to be able to find themselves. + include_dirs = [ "include" ] +} + +static_library("gmock") { + # TODO http://crbug.com/412064 enable this flag all the time. + testonly = !is_component_build + sources = [ + # Sources based on files in r173 of gmock. + "include/gmock/gmock-actions.h", + "include/gmock/gmock-cardinalities.h", + "include/gmock/gmock-generated-actions.h", + "include/gmock/gmock-generated-function-mockers.h", + "include/gmock/gmock-generated-matchers.h", + "include/gmock/gmock-generated-nice-strict.h", + "include/gmock/gmock-matchers.h", + "include/gmock/gmock-spec-builders.h", + "include/gmock/gmock.h", + "include/gmock/internal/gmock-generated-internal-utils.h", + "include/gmock/internal/gmock-internal-utils.h", + "include/gmock/internal/gmock-port.h", + + #"src/gmock-all.cc", # Not needed by our build. + "src/gmock-cardinalities.cc", + "src/gmock-internal-utils.cc", + "src/gmock-matchers.cc", + "src/gmock-spec-builders.cc", + "src/gmock.cc", + ] + + # This project includes some stuff form gtest's guts. + include_dirs = [ "../gtest/include" ] + + public_configs = [ + ":gmock_config", + "//testing/gtest:gtest_config", + ] +} + +static_library("gmock_main") { + # TODO http://crbug.com/412064 enable this flag all the time. + testonly = !is_component_build + sources = [ + "src/gmock_main.cc", + ] + deps = [ + ":gmock", + ] +} diff --git a/engine/src/flutter/build/secondary/testing/gtest/BUILD.gn b/engine/src/flutter/build/secondary/testing/gtest/BUILD.gn new file mode 100644 index 0000000000..a20a09d4db --- /dev/null +++ b/engine/src/flutter/build/secondary/testing/gtest/BUILD.gn @@ -0,0 +1,125 @@ +# Copyright 2014 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. + +config("gtest_config") { + visibility = [ + ":*", + "//testing/gmock:*", # gmock also shares this config. + ] + + defines = [ + # In order to allow regex matches in gtest to be shared between Windows + # and other systems, we tell gtest to always use it's internal engine. + "GTEST_HAS_POSIX_RE=0", + + # Chrome doesn't support / require C++11, yet. + "GTEST_LANG_CXX11=0", + ] + + # Gtest headers need to be able to find themselves. + include_dirs = [ "include" ] + + if (is_win) { + cflags = [ "/wd4800" ] # Unused variable warning. + } + + if (is_posix) { + defines += [ + # gtest isn't able to figure out when RTTI is disabled for gcc + # versions older than 4.3.2, and assumes it's enabled. Our Mac + # and Linux builds disable RTTI, and cannot guarantee that the + # compiler will be 4.3.2. or newer. The Mac, for example, uses + # 4.2.1 as that is the latest available on that platform. gtest + # must be instructed that RTTI is disabled here, and for any + # direct dependents that might include gtest headers. + "GTEST_HAS_RTTI=0", + ] + } + + if (is_android) { + defines += [ + # We want gtest features that use tr1::tuple, but we currently + # don't support the variadic templates used by libstdc++'s + # implementation. gtest supports this scenario by providing its + # own implementation but we must opt in to it. + "GTEST_USE_OWN_TR1_TUPLE=1", + + # GTEST_USE_OWN_TR1_TUPLE only works if GTEST_HAS_TR1_TUPLE is set. + # gtest r625 made it so that GTEST_HAS_TR1_TUPLE is set to 0 + # automatically on android, so it has to be set explicitly here. + "GTEST_HAS_TR1_TUPLE=1", + ] + } +} + +config("gtest_direct_config") { + visibility = [ ":*" ] + defines = [ "UNIT_TEST" ] +} + +static_library("gtest") { + # TODO http://crbug.com/412064 enable this flag all the time. + testonly = !is_component_build + sources = [ + "include/gtest/gtest-death-test.h", + "include/gtest/gtest-message.h", + "include/gtest/gtest-param-test.h", + "include/gtest/gtest-printers.h", + "include/gtest/gtest-spi.h", + "include/gtest/gtest-test-part.h", + "include/gtest/gtest-typed-test.h", + "include/gtest/gtest.h", + "include/gtest/gtest_pred_impl.h", + "include/gtest/internal/gtest-death-test-internal.h", + "include/gtest/internal/gtest-filepath.h", + "include/gtest/internal/gtest-internal.h", + "include/gtest/internal/gtest-linked_ptr.h", + "include/gtest/internal/gtest-param-util-generated.h", + "include/gtest/internal/gtest-param-util.h", + "include/gtest/internal/gtest-port.h", + "include/gtest/internal/gtest-string.h", + "include/gtest/internal/gtest-tuple.h", + "include/gtest/internal/gtest-type-util.h", + + #"gtest/src/gtest-all.cc", # Not needed by our build. + "../multiprocess_func_list.cc", + "../multiprocess_func_list.h", + "../platform_test.h", + "src/gtest-death-test.cc", + "src/gtest-filepath.cc", + "src/gtest-internal-inl.h", + "src/gtest-port.cc", + "src/gtest-printers.cc", + "src/gtest-test-part.cc", + "src/gtest-typed-test.cc", + "src/gtest.cc", + ] + + if (is_mac) { + sources += [ + "../gtest_mac.h", + "../gtest_mac.mm", + "../platform_test_mac.mm", + ] + } + + include_dirs = [ "." ] + + all_dependent_configs = [ ":gtest_config" ] + public_configs = [ ":gtest_direct_config" ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} + +source_set("gtest_main") { + # TODO http://crbug.com/412064 enable this flag all the time. + testonly = !is_component_build + sources = [ + "src/gtest_main.cc", + ] + deps = [ + ":gtest", + ] +} diff --git a/engine/src/flutter/build/secondary/third_party/android_tools/BUILD.gn b/engine/src/flutter/build/secondary/third_party/android_tools/BUILD.gn new file mode 100644 index 0000000000..dc934cb93e --- /dev/null +++ b/engine/src/flutter/build/secondary/third_party/android_tools/BUILD.gn @@ -0,0 +1,94 @@ +# Copyright 2014 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. + +import("//build/config/android/rules.gni") + +config("cpu_features_include") { + include_dirs = [ "ndk/sources/android/cpufeatures" ] +} + +# This is the GN version of +# //build/android/ndk.gyp:cpu_features +source_set("cpu_features") { + sources = [ + "ndk/sources/android/cpufeatures/cpu-features.c", + ] + public_configs = [ ":cpu_features_include" ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} + +android_java_prebuilt("android_gcm_java") { + jar_path = "$android_sdk_root/extras/google/gcm/gcm-client/dist/gcm.jar" +} + +android_java_prebuilt("uiautomator_java") { + jar_path = "$android_sdk/uiautomator.jar" +} + +android_java_prebuilt("android_support_v13_java") { + jar_path = + "$android_sdk_root/extras/android/support/v13/android-support-v13.jar" +} + +android_resources("android_support_v7_appcompat_resources") { + v14_skip = true + resource_dirs = + [ "$android_sdk_root/extras/android/support/v7/appcompat/res" ] + custom_package = "android.support.v7.appcompat" +} + +android_java_prebuilt("android_support_v7_appcompat_java") { + deps = [ + ":android_support_v7_appcompat_resources", + ] + jar_path = "$android_sdk_root/extras/android/support/v7/appcompat/libs/android-support-v7-appcompat.jar" +} + +android_resources("android_support_v7_mediarouter_resources") { + v14_skip = true + resource_dirs = + [ "$android_sdk_root/extras/android/support/v7/mediarouter/res" ] + deps = [ + ":android_support_v7_appcompat_resources", + ] + custom_package = "android.support.v7.mediarouter" +} + +android_java_prebuilt("android_support_v7_mediarouter_java") { + deps = [ + ":android_support_v7_mediarouter_resources", + ":android_support_v7_appcompat_java", + ] + jar_path = "$android_sdk_root/extras/android/support/v7/mediarouter/libs/android-support-v7-mediarouter.jar" +} + +android_resources("android_support_v7_recyclerview_resources") { + v14_skip = true + resource_dirs = + [ "$android_sdk_root/extras/android/support/v7/recyclerview/res" ] + custom_package = "android.support.v7.recyclerview" +} + +android_java_prebuilt("android_support_v7_recyclerview_java") { + deps = [ + ":android_support_v7_appcompat_java", + ":android_support_v7_recyclerview_resources", + ] + jar_path = "$android_sdk_root/extras/android/support/v7/recyclerview/libs/android-support-v7-recyclerview.jar" +} + +android_resources("google_play_services_default_resources") { + v14_skip = true + resource_dirs = [ "$android_sdk_root/extras/google/google_play_services/libproject/google-play-services_lib/res" ] + custom_package = "com.google.android.gms" +} +android_java_prebuilt("google_play_services_default_java") { + deps = [ + ":android_support_v13_java", + ":google_play_services_default_resources", + ] + jar_path = "$android_sdk_root/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar" +} diff --git a/engine/src/flutter/build/secondary/third_party/cacheinvalidation/BUILD.gn b/engine/src/flutter/build/secondary/third_party/cacheinvalidation/BUILD.gn new file mode 100644 index 0000000000..17e4d1c4a1 --- /dev/null +++ b/engine/src/flutter/build/secondary/third_party/cacheinvalidation/BUILD.gn @@ -0,0 +1,146 @@ +# Copyright 2014 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. + +import("//testing/test.gni") + +config("cacheinvalidation_config") { + include_dirs = [ + "overrides", + "src", + ] +} + +static_library("cacheinvalidation") { + sources = [ + "overrides/google/cacheinvalidation/deps/callback.h", + "overrides/google/cacheinvalidation/deps/gmock.h", + "overrides/google/cacheinvalidation/deps/googletest.h", + "overrides/google/cacheinvalidation/deps/logging.h", + "overrides/google/cacheinvalidation/deps/mutex.h", + "overrides/google/cacheinvalidation/deps/random.cc", + "overrides/google/cacheinvalidation/deps/random.h", + "overrides/google/cacheinvalidation/deps/scoped_ptr.h", + "overrides/google/cacheinvalidation/deps/sha1-digest-function.h", + "overrides/google/cacheinvalidation/deps/stl-namespace.h", + "overrides/google/cacheinvalidation/deps/string_util.h", + "overrides/google/cacheinvalidation/deps/time.h", + "src/google/cacheinvalidation/deps/digest-function.h", + "src/google/cacheinvalidation/impl/basic-system-resources.cc", + "src/google/cacheinvalidation/impl/basic-system-resources.h", + "src/google/cacheinvalidation/impl/checking-invalidation-listener.cc", + "src/google/cacheinvalidation/impl/checking-invalidation-listener.h", + "src/google/cacheinvalidation/impl/client-protocol-namespace-fix.h", + "src/google/cacheinvalidation/impl/constants.cc", + "src/google/cacheinvalidation/impl/constants.h", + "src/google/cacheinvalidation/impl/digest-store.h", + "src/google/cacheinvalidation/impl/exponential-backoff-delay-generator.cc", + "src/google/cacheinvalidation/impl/exponential-backoff-delay-generator.h", + "src/google/cacheinvalidation/impl/invalidation-client-core.cc", + "src/google/cacheinvalidation/impl/invalidation-client-core.h", + "src/google/cacheinvalidation/impl/invalidation-client-factory.cc", + "src/google/cacheinvalidation/impl/invalidation-client-impl.cc", + "src/google/cacheinvalidation/impl/invalidation-client-impl.h", + "src/google/cacheinvalidation/impl/invalidation-client-util.h", + "src/google/cacheinvalidation/impl/log-macro.h", + "src/google/cacheinvalidation/impl/object-id-digest-utils.cc", + "src/google/cacheinvalidation/impl/object-id-digest-utils.h", + "src/google/cacheinvalidation/impl/persistence-utils.cc", + "src/google/cacheinvalidation/impl/persistence-utils.h", + "src/google/cacheinvalidation/impl/proto-converter.cc", + "src/google/cacheinvalidation/impl/proto-converter.h", + "src/google/cacheinvalidation/impl/proto-helpers.cc", + "src/google/cacheinvalidation/impl/proto-helpers.h", + "src/google/cacheinvalidation/impl/protocol-handler.cc", + "src/google/cacheinvalidation/impl/protocol-handler.h", + "src/google/cacheinvalidation/impl/recurring-task.cc", + "src/google/cacheinvalidation/impl/recurring-task.h", + "src/google/cacheinvalidation/impl/registration-manager.cc", + "src/google/cacheinvalidation/impl/registration-manager.h", + "src/google/cacheinvalidation/impl/repeated-field-namespace-fix.h", + "src/google/cacheinvalidation/impl/run-state.h", + "src/google/cacheinvalidation/impl/safe-storage.cc", + "src/google/cacheinvalidation/impl/safe-storage.h", + "src/google/cacheinvalidation/impl/simple-registration-store.cc", + "src/google/cacheinvalidation/impl/simple-registration-store.h", + "src/google/cacheinvalidation/impl/smearer.h", + "src/google/cacheinvalidation/impl/statistics.cc", + "src/google/cacheinvalidation/impl/statistics.h", + "src/google/cacheinvalidation/impl/throttle.cc", + "src/google/cacheinvalidation/impl/throttle.h", + "src/google/cacheinvalidation/impl/ticl-message-validator.cc", + "src/google/cacheinvalidation/impl/ticl-message-validator.h", + "src/google/cacheinvalidation/include/invalidation-client-factory.h", + "src/google/cacheinvalidation/include/invalidation-client.h", + "src/google/cacheinvalidation/include/invalidation-listener.h", + "src/google/cacheinvalidation/include/system-resources.h", + "src/google/cacheinvalidation/include/types.h", + ] + + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] + public_configs = [ ":cacheinvalidation_config" ] + + public_deps = [ + "src/google/cacheinvalidation:cacheinvalidation_proto_cpp", + ] + + deps = [ + "//base", + ] +} + +test("cacheinvalidation_unittests") { + sources = [ + "src/google/cacheinvalidation/impl/invalidation-client-impl_test.cc", + "src/google/cacheinvalidation/impl/protocol-handler_test.cc", + "src/google/cacheinvalidation/impl/recurring-task_test.cc", + "src/google/cacheinvalidation/impl/throttle_test.cc", + "src/google/cacheinvalidation/test/deterministic-scheduler.cc", + "src/google/cacheinvalidation/test/deterministic-scheduler.h", + "src/google/cacheinvalidation/test/test-logger.cc", + "src/google/cacheinvalidation/test/test-logger.h", + "src/google/cacheinvalidation/test/test-utils.cc", + "src/google/cacheinvalidation/test/test-utils.h", + ] + + deps = [ + ":cacheinvalidation", + "src/google/cacheinvalidation:cacheinvalidation_proto_cpp", + "//base", + "//base/test:run_all_unittests", + "//testing/gmock", + "//testing/gtest", + ] +} + +# TODO(GYP) Test isolation stuff. +if (is_android) { + import("//build/config/android/rules.gni") + + # GYP: //third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_proto_java + proto_java_library("cacheinvalidation_proto_java") { + proto_path = "src/proto" + sources = [ + "$proto_path/android_channel.proto", + "$proto_path/android_listener.proto", + "$proto_path/android_service.proto", + "$proto_path/channel_common.proto", + "$proto_path/client.proto", + "$proto_path/client_protocol.proto", + "$proto_path/java_client.proto", + "$proto_path/types.proto", + ] + } + + # GYP: //third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_javalib + android_library("cacheinvalidation_javalib") { + deps = [ + ":cacheinvalidation_proto_java", + "//third_party/android_protobuf:protobuf_nano_javalib", + "//third_party/android_tools:android_gcm_java", + ] + + DEPRECATED_java_in_dir = "src/java" + } +} diff --git a/engine/src/flutter/build/secondary/third_party/cacheinvalidation/src/google/cacheinvalidation/BUILD.gn b/engine/src/flutter/build/secondary/third_party/cacheinvalidation/src/google/cacheinvalidation/BUILD.gn new file mode 100644 index 0000000000..3bbb844eea --- /dev/null +++ b/engine/src/flutter/build/secondary/third_party/cacheinvalidation/src/google/cacheinvalidation/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright 2014 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. + +import("//third_party/protobuf/proto_library.gni") + +proto_library("cacheinvalidation_proto_cpp") { + # Depend on cacheinvalidation instead. + visibility = [ "//third_party/cacheinvalidation/*" ] + + sources = [ + "client.proto", + "client_gateway.proto", + "client_protocol.proto", + "client_test_internal.proto", + "types.proto", + ] + + if (!is_android) { + sources += [ + "android_channel.proto", + "channel_common.proto", + ] + } + + proto_out_dir = "google/cacheinvalidation" +} diff --git a/engine/src/flutter/build/secondary/third_party/libjpeg_turbo/BUILD.gn b/engine/src/flutter/build/secondary/third_party/libjpeg_turbo/BUILD.gn new file mode 100644 index 0000000000..bf35d07eaa --- /dev/null +++ b/engine/src/flutter/build/secondary/third_party/libjpeg_turbo/BUILD.gn @@ -0,0 +1,211 @@ +# Copyright 2014 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. + +# Do not use the targets in this file unless you need a certain libjpeg +# implementation. Use the meta target //third_party:jpeg instead. + +if (current_cpu == "arm") { + import("//build/config/arm.gni") +} + +if (current_cpu == "x86" || current_cpu == "x64") { + import("//third_party/yasm/yasm_assemble.gni") + + yasm_assemble("simd_asm") { + defines = [] + + if (current_cpu == "x86") { + sources = [ + "simd/jccolmmx.asm", + "simd/jccolss2.asm", + "simd/jcgrammx.asm", + "simd/jcgrass2.asm", + "simd/jcqnt3dn.asm", + "simd/jcqntmmx.asm", + "simd/jcqnts2f.asm", + "simd/jcqnts2i.asm", + "simd/jcqntsse.asm", + "simd/jcsammmx.asm", + "simd/jcsamss2.asm", + "simd/jdcolmmx.asm", + "simd/jdcolss2.asm", + "simd/jdmermmx.asm", + "simd/jdmerss2.asm", + "simd/jdsammmx.asm", + "simd/jdsamss2.asm", + "simd/jf3dnflt.asm", + "simd/jfmmxfst.asm", + "simd/jfmmxint.asm", + "simd/jfss2fst.asm", + "simd/jfss2int.asm", + "simd/jfsseflt.asm", + "simd/ji3dnflt.asm", + "simd/jimmxfst.asm", + "simd/jimmxint.asm", + "simd/jimmxred.asm", + "simd/jiss2flt.asm", + "simd/jiss2fst.asm", + "simd/jiss2int.asm", + "simd/jiss2red.asm", + "simd/jisseflt.asm", + "simd/jsimdcpu.asm", + ] + defines += [ "__x86__" ] + } else if (current_cpu == "x64") { + sources = [ + "simd/jccolss2-64.asm", + "simd/jcgrass2-64.asm", + "simd/jcqnts2f-64.asm", + "simd/jcqnts2i-64.asm", + "simd/jcsamss2-64.asm", + "simd/jdcolss2-64.asm", + "simd/jdmerss2-64.asm", + "simd/jdsamss2-64.asm", + "simd/jfss2fst-64.asm", + "simd/jfss2int-64.asm", + "simd/jfsseflt-64.asm", + "simd/jiss2flt-64.asm", + "simd/jiss2fst-64.asm", + "simd/jiss2int-64.asm", + "simd/jiss2red-64.asm", + ] + defines += [ "__x86_64__" ] + } + + if (is_win) { + defines += [ "MSVC" ] + include_dirs = [ "win" ] + if (current_cpu == "x86") { + defines += [ "WIN32" ] + } else { + defines += [ "WIN64" ] + } + } else if (is_mac) { + defines += [ "MACHO" ] + include_dirs = [ "mac" ] + } else if (is_linux || is_android) { + defines += [ "ELF" ] + include_dirs = [ "linux" ] + } + } +} + +source_set("simd") { + if (current_cpu == "x86") { + deps = [ + ":simd_asm", + ] + sources = [ + "simd/jsimd_i386.c", + ] + if (is_win) { + cflags = [ "/wd4245" ] + } + } else if (current_cpu == "x64") { + deps = [ + ":simd_asm", + ] + sources = [ + "simd/jsimd_x86_64.c", + ] + } else if (current_cpu == "arm" && arm_version >= 7 && + (arm_use_neon || arm_optionally_use_neon)) { + sources = [ + "simd/jsimd_arm.c", + "simd/jsimd_arm_neon.S", + ] + } else { + sources = [ + "jsimd_none.c", + ] + } +} + +config("libjpeg_config") { + include_dirs = [ "." ] +} + +source_set("libjpeg") { + sources = [ + "jcapimin.c", + "jcapistd.c", + "jccoefct.c", + "jccolor.c", + "jcdctmgr.c", + "jchuff.c", + "jchuff.h", + "jcinit.c", + "jcmainct.c", + "jcmarker.c", + "jcmaster.c", + "jcomapi.c", + "jconfig.h", + "jcparam.c", + "jcphuff.c", + "jcprepct.c", + "jcsample.c", + "jdapimin.c", + "jdapistd.c", + "jdatadst.c", + "jdatasrc.c", + "jdcoefct.c", + "jdcolor.c", + "jdct.h", + "jddctmgr.c", + "jdhuff.c", + "jdhuff.h", + "jdinput.c", + "jdmainct.c", + "jdmarker.c", + "jdmaster.c", + "jdmerge.c", + "jdphuff.c", + "jdpostct.c", + "jdsample.c", + "jerror.c", + "jerror.h", + "jfdctflt.c", + "jfdctfst.c", + "jfdctint.c", + "jidctflt.c", + "jidctfst.c", + "jidctint.c", + "jidctred.c", + "jinclude.h", + "jmemmgr.c", + "jmemnobs.c", + "jmemsys.h", + "jmorecfg.h", + "jpegint.h", + "jpeglib.h", + "jpeglibmangler.h", + "jquant1.c", + "jquant2.c", + "jutils.c", + "jversion.h", + ] + + defines = [ + "WITH_SIMD", + "MOTION_JPEG_SUPPORTED", + "NO_GETENV", + ] + + configs += [ ":libjpeg_config" ] + + public_configs = [ ":libjpeg_config" ] + + # MemorySanitizer doesn't support assembly code, so keep it disabled in + # MSan builds for now. + # TODO: Enable on Linux when .asm files are recognized. + if (is_msan || is_linux) { + sources += [ "jsimd_none.c" ] + } else { + deps = [ + ":simd", + ] + } + + # TODO(GYP): Compile the .asm files with YASM as GYP does. +} diff --git a/engine/src/flutter/build/secondary/third_party/libsrtp/BUILD.gn b/engine/src/flutter/build/secondary/third_party/libsrtp/BUILD.gn new file mode 100644 index 0000000000..7601bea0d6 --- /dev/null +++ b/engine/src/flutter/build/secondary/third_party/libsrtp/BUILD.gn @@ -0,0 +1,391 @@ +# Copyright 2014 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. + +declare_args() { + use_system_libsrtp = false + use_srtp_boringssl = true +} + +config("libsrtp_config") { + defines = [ + "HAVE_CONFIG_H", + "HAVE_STDLIB_H", + "HAVE_STRING_H", + "TESTAPP_SOURCE", + ] + + include_dirs = [ + "config", + "srtp/include", + "srtp/crypto/include", + ] + + if (use_srtp_boringssl) { + defines += [ "OPENSSL" ] + } + + if (is_posix) { + defines += [ + "HAVE_INT16_T", + "HAVE_INT32_T", + "HAVE_INT8_T", + "HAVE_UINT16_T", + "HAVE_UINT32_T", + "HAVE_UINT64_T", + "HAVE_UINT8_T", + "HAVE_STDINT_H", + "HAVE_INTTYPES_H", + "HAVE_NETINET_IN_H", + "HAVE_ARPA_INET_H", + "HAVE_UNISTD_H", + ] + cflags = [ "-Wno-unused-variable" ] + } + + if (is_win) { + defines += [ + "HAVE_BYTESWAP_METHODS_H", + + # All Windows architectures are this way. + "SIZEOF_UNSIGNED_LONG=4", + "SIZEOF_UNSIGNED_LONG_LONG=8", + ] + } + + if (current_cpu == "x64" || current_cpu == "x86" || current_cpu == "arm") { + defines += [ + # TODO(leozwang): CPU_RISC doesn"t work properly on android/arm + # platform for unknown reasons, need to investigate the root cause + # of it. CPU_RISC is used for optimization only, and CPU_CISC should + # just work just fine, it has been tested on android/arm with srtp + # test applications and libjingle. + "CPU_CISC", + ] + } + + if (current_cpu == "mipsel") { + defines += [ "CPU_RISC" ] + } +} + +config("system_libsrtp_config") { + defines = [ "USE_SYSTEM_LIBSRTP" ] + include_dirs = [ "/usr/include/srtp" ] +} + +if (use_system_libsrtp) { + group("libsrtp") { + public_configs = [ + ":libsrtp_config", + ":system_libsrtp_config", + ] + libs = [ "-lsrtp" ] + } +} else { + static_library("libsrtp") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + public_configs = [ ":libsrtp_config" ] + + sources = [ + # includes + "srtp/include/ekt.h", + "srtp/include/getopt_s.h", + "srtp/include/rtp.h", + "srtp/include/rtp_priv.h", + "srtp/include/srtp.h", + "srtp/include/srtp_priv.h", + "srtp/include/ut_sim.h", + + # headers + "srtp/crypto/include/aes.h", + "srtp/crypto/include/aes_cbc.h", + "srtp/crypto/include/aes_icm.h", + "srtp/crypto/include/alloc.h", + "srtp/crypto/include/auth.h", + "srtp/crypto/include/cipher.h", + "srtp/crypto/include/crypto.h", + "srtp/crypto/include/crypto_kernel.h", + "srtp/crypto/include/crypto_math.h", + "srtp/crypto/include/crypto_types.h", + "srtp/crypto/include/cryptoalg.h", + "srtp/crypto/include/datatypes.h", + "srtp/crypto/include/err.h", + "srtp/crypto/include/gf2_8.h", + "srtp/crypto/include/hmac.h", + "srtp/crypto/include/integers.h", + "srtp/crypto/include/kernel_compat.h", + "srtp/crypto/include/key.h", + "srtp/crypto/include/null_auth.h", + "srtp/crypto/include/null_cipher.h", + "srtp/crypto/include/prng.h", + "srtp/crypto/include/rand_source.h", + "srtp/crypto/include/rdb.h", + "srtp/crypto/include/rdbx.h", + "srtp/crypto/include/sha1.h", + "srtp/crypto/include/stat.h", + "srtp/crypto/include/xfm.h", + + # sources + "srtp/crypto/cipher/aes.c", + "srtp/crypto/cipher/aes_cbc.c", + "srtp/crypto/cipher/aes_icm.c", + "srtp/crypto/cipher/cipher.c", + "srtp/crypto/cipher/null_cipher.c", + "srtp/crypto/hash/auth.c", + "srtp/crypto/hash/hmac.c", + "srtp/crypto/hash/null_auth.c", + "srtp/crypto/hash/sha1.c", + "srtp/crypto/kernel/alloc.c", + "srtp/crypto/kernel/crypto_kernel.c", + "srtp/crypto/kernel/err.c", + "srtp/crypto/kernel/key.c", + "srtp/crypto/math/datatypes.c", + "srtp/crypto/math/gf2_8.c", + "srtp/crypto/math/stat.c", + "srtp/crypto/replay/rdb.c", + "srtp/crypto/replay/rdbx.c", + "srtp/crypto/replay/ut_sim.c", + "srtp/crypto/rng/ctr_prng.c", + "srtp/crypto/rng/prng.c", + "srtp/crypto/rng/rand_source.c", + "srtp/srtp/ekt.c", + "srtp/srtp/srtp.c", + ] + + if (is_clang) { + cflags = [ "-Wno-implicit-function-declaration" ] + } + + if (use_srtp_boringssl) { + deps = [ + "//third_party/boringssl:boringssl", + ] + public_deps = [ + "//third_party/boringssl:boringssl", + ] + sources -= [ + "srtp/crypto/cipher/aes_cbc.c", + "srtp/crypto/cipher/aes_icm.c", + "srtp/crypto/hash/hmac.c", + "srtp/crypto/hash/sha1.c", + "srtp/crypto/rng/ctr_prng.c", + "srtp/crypto/rng/prng.c", + ] + sources += [ + "srtp/crypto/cipher/aes_gcm_ossl.c", + "srtp/crypto/cipher/aes_icm_ossl.c", + "srtp/crypto/hash/hmac_ossl.c", + "srtp/crypto/include/aes_gcm_ossl.h", + "srtp/crypto/include/aes_icm_ossl.h", + ] + } + } + + # TODO(GYP): A bunch of these tests don't compile (in gyp either). They're + # not very broken, so could probably be made to work if it's useful. + if (!is_win) { + executable("rdbx_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + "srtp/test/rdbx_driver.c", + ] + } + + executable("srtp_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/include/getopt_s.h", + "srtp/include/srtp_priv.h", + "srtp/test/getopt_s.c", + "srtp/test/srtp_driver.c", + ] + } + + executable("roc_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/include/rdbx.h", + "srtp/include/ut_sim.h", + "srtp/test/roc_driver.c", + ] + } + + executable("replay_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/include/rdbx.h", + "srtp/include/ut_sim.h", + "srtp/test/replay_driver.c", + ] + } + + executable("rtpw") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/include/datatypes.h", + "srtp/include/getopt_s.h", + "srtp/include/rtp.h", + "srtp/include/srtp.h", + "srtp/test/getopt_s.c", + "srtp/test/rtp.c", + "srtp/test/rtpw.c", + ] + if (is_android) { + defines = [ "HAVE_SYS_SOCKET_H" ] + } + if (is_clang) { + cflags = [ "-Wno-implicit-function-declaration" ] + } + } + + executable("srtp_test_cipher_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/test/cipher_driver.c", + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + ] + } + + executable("srtp_test_datatypes_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/test/datatypes_driver.c", + ] + } + + executable("srtp_test_stat_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/test/stat_driver.c", + ] + } + + executable("srtp_test_sha1_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/test/sha1_driver.c", + ] + } + + executable("srtp_test_kernel_driver") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/test/kernel_driver.c", + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + ] + } + + executable("srtp_test_aes_calc") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/test/aes_calc.c", + ] + } + + executable("srtp_test_rand_gen") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/test/rand_gen.c", + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + ] + } + + executable("srtp_test_rand_gen_soak") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/test/rand_gen_soak.c", + "srtp/include/getopt_s.h", + "srtp/test/getopt_s.c", + ] + } + + executable("srtp_test_env") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + deps = [ + ":libsrtp", + ] + sources = [ + "srtp/crypto/test/env.c", + ] + } + + group("srtp_runtest") { + deps = [ + ":rdbx_driver", + ":srtp_driver", + ":roc_driver", + ":replay_driver", + ":rtpw", + ":srtp_test_cipher_driver", + ":srtp_test_datatypes_driver", + ":srtp_test_stat_driver", + ":srtp_test_sha1_driver", + ":srtp_test_kernel_driver", + ":srtp_test_aes_calc", + ":srtp_test_rand_gen", + ":srtp_test_rand_gen_soak", + ":srtp_test_env", + ] + } + } +} diff --git a/engine/src/flutter/build/secondary/third_party/nss/BUILD.gn b/engine/src/flutter/build/secondary/third_party/nss/BUILD.gn new file mode 100644 index 0000000000..25d449e14d --- /dev/null +++ b/engine/src/flutter/build/secondary/third_party/nss/BUILD.gn @@ -0,0 +1,1211 @@ +# Copyright 2014 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. + +import("//build/config/linux/pkg_config.gni") + +if (is_linux) { + # This is a dependency on NSS with no libssl. On Linux we use a built-in SSL + # library but the system NSS libraries. Non-Linux platforms using NSS use the + # hermetic one in //third_party/nss. + # + # Generally you should depend on //crypto:platform instead of using this + # config since that will properly pick up NSS or OpenSSL depending on + # platform and build config. + pkg_config("system_nss_no_ssl_config") { + packages = [ "nss" ] + extra_args = [ + "-v", + "-lssl3", + ] + } +} else { + include_nss_root_certs = is_ios + include_nss_libpkix = is_ios + + config("nspr_config") { + defines = [ "NO_NSPR_10_SUPPORT" ] + include_dirs = [ + "nspr/pr/include", + "nspr/lib/ds", + "nspr/lib/libc/include", + ] + + if (component_mode != "shared_library") { + defines += [ "NSPR_STATIC" ] + } + } + + component("nspr") { + output_name = "crnspr" + sources = [ + "nspr/lib/ds/plarena.c", + "nspr/lib/ds/plarena.h", + "nspr/lib/ds/plarenas.h", + "nspr/lib/ds/plhash.c", + "nspr/lib/ds/plhash.h", + "nspr/lib/libc/include/plbase64.h", + "nspr/lib/libc/include/plerror.h", + "nspr/lib/libc/include/plgetopt.h", + "nspr/lib/libc/include/plstr.h", + "nspr/lib/libc/src/base64.c", + "nspr/lib/libc/src/plerror.c", + "nspr/lib/libc/src/plgetopt.c", + "nspr/lib/libc/src/strcase.c", + "nspr/lib/libc/src/strcat.c", + "nspr/lib/libc/src/strchr.c", + "nspr/lib/libc/src/strcmp.c", + "nspr/lib/libc/src/strcpy.c", + "nspr/lib/libc/src/strdup.c", + "nspr/lib/libc/src/strlen.c", + "nspr/lib/libc/src/strpbrk.c", + "nspr/lib/libc/src/strstr.c", + "nspr/lib/libc/src/strtok.c", + "nspr/pr/include/md/_darwin.cfg", + "nspr/pr/include/md/_darwin.h", + "nspr/pr/include/md/_pcos.h", + "nspr/pr/include/md/_pth.h", + "nspr/pr/include/md/_unix_errors.h", + "nspr/pr/include/md/_unixos.h", + "nspr/pr/include/md/_win32_errors.h", + "nspr/pr/include/md/_win95.cfg", + "nspr/pr/include/md/_win95.h", + "nspr/pr/include/md/prosdep.h", + "nspr/pr/include/nspr.h", + "nspr/pr/include/obsolete/pralarm.h", + "nspr/pr/include/obsolete/probslet.h", + "nspr/pr/include/obsolete/protypes.h", + "nspr/pr/include/obsolete/prsem.h", + "nspr/pr/include/pratom.h", + "nspr/pr/include/prbit.h", + "nspr/pr/include/prclist.h", + "nspr/pr/include/prcmon.h", + "nspr/pr/include/prcountr.h", + "nspr/pr/include/prcpucfg.h", + "nspr/pr/include/prcvar.h", + "nspr/pr/include/prdtoa.h", + "nspr/pr/include/prenv.h", + "nspr/pr/include/prerr.h", + "nspr/pr/include/prerror.h", + "nspr/pr/include/prinet.h", + "nspr/pr/include/prinit.h", + "nspr/pr/include/prinrval.h", + "nspr/pr/include/prio.h", + "nspr/pr/include/pripcsem.h", + "nspr/pr/include/private/pprio.h", + "nspr/pr/include/private/pprmwait.h", + "nspr/pr/include/private/pprthred.h", + "nspr/pr/include/private/primpl.h", + "nspr/pr/include/private/prpriv.h", + "nspr/pr/include/prlink.h", + "nspr/pr/include/prlock.h", + "nspr/pr/include/prlog.h", + "nspr/pr/include/prlong.h", + "nspr/pr/include/prmem.h", + "nspr/pr/include/prmon.h", + "nspr/pr/include/prmwait.h", + "nspr/pr/include/prnetdb.h", + "nspr/pr/include/prolock.h", + "nspr/pr/include/prpdce.h", + "nspr/pr/include/prprf.h", + "nspr/pr/include/prproces.h", + "nspr/pr/include/prrng.h", + "nspr/pr/include/prrwlock.h", + "nspr/pr/include/prshm.h", + "nspr/pr/include/prshma.h", + "nspr/pr/include/prsystem.h", + "nspr/pr/include/prthread.h", + "nspr/pr/include/prtime.h", + "nspr/pr/include/prtpool.h", + "nspr/pr/include/prtrace.h", + "nspr/pr/include/prtypes.h", + "nspr/pr/include/prvrsion.h", + "nspr/pr/include/prwin16.h", + "nspr/pr/src/io/prdir.c", + "nspr/pr/src/io/prfdcach.c", + "nspr/pr/src/io/prfile.c", + "nspr/pr/src/io/prio.c", + "nspr/pr/src/io/priometh.c", + "nspr/pr/src/io/pripv6.c", + "nspr/pr/src/io/prlayer.c", + "nspr/pr/src/io/prlog.c", + "nspr/pr/src/io/prmapopt.c", + "nspr/pr/src/io/prmmap.c", + "nspr/pr/src/io/prmwait.c", + "nspr/pr/src/io/prpolevt.c", + "nspr/pr/src/io/prprf.c", + "nspr/pr/src/io/prscanf.c", + "nspr/pr/src/io/prsocket.c", + "nspr/pr/src/io/prstdio.c", + "nspr/pr/src/linking/prlink.c", + "nspr/pr/src/malloc/prmalloc.c", + "nspr/pr/src/malloc/prmem.c", + "nspr/pr/src/md/prosdep.c", + "nspr/pr/src/md/unix/darwin.c", + "nspr/pr/src/md/unix/os_Darwin.s", + "nspr/pr/src/md/unix/unix.c", + "nspr/pr/src/md/unix/unix_errors.c", + "nspr/pr/src/md/unix/uxproces.c", + "nspr/pr/src/md/unix/uxrng.c", + "nspr/pr/src/md/unix/uxshm.c", + "nspr/pr/src/md/unix/uxwrap.c", + "nspr/pr/src/md/windows/ntgc.c", + "nspr/pr/src/md/windows/ntinrval.c", + "nspr/pr/src/md/windows/ntmisc.c", + "nspr/pr/src/md/windows/ntsec.c", + "nspr/pr/src/md/windows/ntsem.c", + "nspr/pr/src/md/windows/w32ipcsem.c", + "nspr/pr/src/md/windows/w32poll.c", + "nspr/pr/src/md/windows/w32rng.c", + "nspr/pr/src/md/windows/w32shm.c", + "nspr/pr/src/md/windows/w95cv.c", + "nspr/pr/src/md/windows/w95dllmain.c", + "nspr/pr/src/md/windows/w95io.c", + "nspr/pr/src/md/windows/w95sock.c", + "nspr/pr/src/md/windows/w95thred.c", + "nspr/pr/src/md/windows/win32_errors.c", + "nspr/pr/src/memory/prseg.c", + "nspr/pr/src/memory/prshm.c", + "nspr/pr/src/memory/prshma.c", + "nspr/pr/src/misc/pralarm.c", + "nspr/pr/src/misc/pratom.c", + "nspr/pr/src/misc/praton.c", + "nspr/pr/src/misc/prcountr.c", + "nspr/pr/src/misc/prdtoa.c", + "nspr/pr/src/misc/prenv.c", + "nspr/pr/src/misc/prerr.c", + "nspr/pr/src/misc/prerror.c", + "nspr/pr/src/misc/prerrortable.c", + "nspr/pr/src/misc/prinit.c", + "nspr/pr/src/misc/prinrval.c", + "nspr/pr/src/misc/pripc.c", + "nspr/pr/src/misc/pripcsem.c", + "nspr/pr/src/misc/prlog2.c", + "nspr/pr/src/misc/prlong.c", + "nspr/pr/src/misc/prnetdb.c", + "nspr/pr/src/misc/prolock.c", + "nspr/pr/src/misc/prrng.c", + "nspr/pr/src/misc/prsystem.c", + "nspr/pr/src/misc/prthinfo.c", + "nspr/pr/src/misc/prtime.c", + "nspr/pr/src/misc/prtpool.c", + "nspr/pr/src/misc/prtrace.c", + "nspr/pr/src/pthreads/ptio.c", + "nspr/pr/src/pthreads/ptmisc.c", + "nspr/pr/src/pthreads/ptsynch.c", + "nspr/pr/src/pthreads/ptthread.c", + "nspr/pr/src/threads/combined/prucpu.c", + "nspr/pr/src/threads/combined/prucv.c", + "nspr/pr/src/threads/combined/prulock.c", + "nspr/pr/src/threads/combined/prustack.c", + "nspr/pr/src/threads/combined/pruthr.c", + "nspr/pr/src/threads/prcmon.c", + "nspr/pr/src/threads/prcthr.c", + "nspr/pr/src/threads/prdump.c", + "nspr/pr/src/threads/prmon.c", + "nspr/pr/src/threads/prrwlock.c", + "nspr/pr/src/threads/prsem.c", + "nspr/pr/src/threads/prtpd.c", + ] + + public_configs = [ ":nspr_config" ] + + configs -= [ "//build/config/compiler:chromium_code" ] + if (is_win) { + configs -= [ + "//build/config/win:unicode", # Requires 8-bit mode. + "//build/config/win:lean_and_mean", # Won"t compile with lean and mean. + ] + } + configs += [ + "//build/config/compiler:no_chromium_code", + "//build/config/compiler:no_size_t_to_int_warning", + ] + + cflags = [] + defines = [ + "_NSPR_BUILD_", + "FORCE_PR_LOG", + ] + + include_dirs = [ "nspr/pr/include/private" ] + + if (is_win) { + cflags = [ "/wd4554" ] # Check precidence. + defines += [ + "XP_PC", + "WIN32", + "WIN95", + "_PR_GLOBAL_THREADS_ONLY", + "_CRT_SECURE_NO_WARNINGS", + ] + } else { + sources -= [ + "nspr/pr/src/md/windows/ntgc.c", + "nspr/pr/src/md/windows/ntinrval.c", + "nspr/pr/src/md/windows/ntmisc.c", + "nspr/pr/src/md/windows/ntsec.c", + "nspr/pr/src/md/windows/ntsem.c", + "nspr/pr/src/md/windows/w32ipcsem.c", + "nspr/pr/src/md/windows/w32poll.c", + "nspr/pr/src/md/windows/w32rng.c", + "nspr/pr/src/md/windows/w32shm.c", + "nspr/pr/src/md/windows/w95cv.c", + "nspr/pr/src/md/windows/w95dllmain.c", + "nspr/pr/src/md/windows/w95io.c", + "nspr/pr/src/md/windows/w95sock.c", + "nspr/pr/src/md/windows/w95thred.c", + "nspr/pr/src/md/windows/win32_errors.c", + "nspr/pr/src/threads/combined/prucpu.c", + "nspr/pr/src/threads/combined/prucv.c", + "nspr/pr/src/threads/combined/prulock.c", + "nspr/pr/src/threads/combined/prustack.c", + "nspr/pr/src/threads/combined/pruthr.c", + ] + } + + if (!is_posix) { + sources -= [ + "nspr/pr/src/md/unix/darwin.c", + "nspr/pr/src/md/unix/os_Darwin.s", + "nspr/pr/src/md/unix/unix.c", + "nspr/pr/src/md/unix/unix_errors.c", + "nspr/pr/src/md/unix/uxproces.c", + "nspr/pr/src/md/unix/uxrng.c", + "nspr/pr/src/md/unix/uxshm.c", + "nspr/pr/src/md/unix/uxwrap.c", + "nspr/pr/src/pthreads/ptio.c", + "nspr/pr/src/pthreads/ptmisc.c", + "nspr/pr/src/pthreads/ptsynch.c", + "nspr/pr/src/pthreads/ptthread.c", + ] + } + + if (current_cpu == "x86") { + defines += [ "_X86_" ] + } else if (current_cpu == "x64") { + defines += [ "_AMD64_" ] + } + + if (is_mac || is_ios) { + sources -= [ + "nspr/pr/src/io/prdir.c", + "nspr/pr/src/io/prfile.c", + "nspr/pr/src/io/prio.c", + "nspr/pr/src/io/prsocket.c", + "nspr/pr/src/misc/pripcsem.c", + "nspr/pr/src/threads/prcthr.c", + "nspr/pr/src/threads/prdump.c", + "nspr/pr/src/threads/prmon.c", + "nspr/pr/src/threads/prsem.c", + ] + defines += [ + "XP_UNIX", + "DARWIN", + "XP_MACOSX", + "_PR_PTHREADS", + "HAVE_BSD_FLOCK", + "HAVE_DLADDR", + "HAVE_LCHOWN", + "HAVE_SOCKLEN_T", + "HAVE_STRERROR", + ] + } + + if (is_mac) { + defines += [ "HAVE_CRT_EXTERNS_H" ] + libs = [ + "CoreFoundation.framework", + "CoreServices.framework", + ] + } + + if (is_clang) { + cflags += [ + # nspr uses a bunch of deprecated functions (NSLinkModule etc) in + # prlink.c on mac. + "-Wno-deprecated-declarations", + + # nspr passes "const char*" through "void*". + "-Wno-incompatible-pointer-types", + + # nspr passes "int*" through "unsigned int*". + "-Wno-pointer-sign", + ] + + # nspr uses assert(!"foo") instead of assert(false && "foo"). + configs -= [ "//build/config/clang:extra_warnings" ] + } + } + + component("nss") { + output_name = "crnss" + sources = [ + # Ensure at least one object file is produced, so that MSVC does not + # warn when creating the static/shared library. See the note for + # the "nssckbi" target for why the "nss" target was split as such. + "nss/lib/nss/nssver.c", + ] + + public_deps = [ + ":nss_static", + ] + + if (include_nss_root_certs) { + public_deps += [ ":nssckbi" ] + } + + if (component_mode == "shared_library") { + if (is_mac) { + ldflags = [ "-all_load" ] + } else if (is_win) { + # Pass the def file to the linker. + ldflags = + [ "/DEF:" + rebase_path("nss/exports_win.def", root_build_dir) ] + } + } + } + + config("nssckbi_config") { + include_dirs = [ "nss/lib/ckfw/builtins" ] + } + + # This is really more of a pseudo-target to work around the fact that + # a single static_library target cannot contain two object files of the + # same name (hash.o / hash.obj). Logically, this is part of the + # "nss_static" target. By separating it out, it creates a possible + # circular dependency between "nss_static" and "nssckbi" when + # "exclude_nss_root_certs" is not specified, as "nss_static" depends on + # the "builtinsC_GetFunctionList" exported by this target. This is an + # artifact of how NSS is being statically built, which is not an + # officially supported configuration - normally, "nssckbi.dll/so" would + # depend on libnss3.dll/so, and the higher layer caller would instruct + # libnss3.dll to dynamically load nssckbi.dll, breaking the circle. + # + # TODO(rsleevi): http://crbug.com/128134 - Break the circular dependency + # without requiring nssckbi to be built as a shared library. + source_set("nssckbi") { + visibility = [ ":nss" ] # This target is internal implementation detail. + + sources = [ + "nss/lib/ckfw/builtins/anchor.c", + "nss/lib/ckfw/builtins/bfind.c", + "nss/lib/ckfw/builtins/binst.c", + "nss/lib/ckfw/builtins/bobject.c", + "nss/lib/ckfw/builtins/bsession.c", + "nss/lib/ckfw/builtins/bslot.c", + "nss/lib/ckfw/builtins/btoken.c", + "nss/lib/ckfw/builtins/builtins.h", + "nss/lib/ckfw/builtins/certdata.c", + "nss/lib/ckfw/builtins/ckbiver.c", + "nss/lib/ckfw/builtins/constants.c", + "nss/lib/ckfw/builtins/nssckbi.h", + "nss/lib/ckfw/ck.h", + "nss/lib/ckfw/ckfw.h", + "nss/lib/ckfw/ckfwm.h", + "nss/lib/ckfw/ckfwtm.h", + "nss/lib/ckfw/ckmd.h", + "nss/lib/ckfw/ckt.h", + "nss/lib/ckfw/crypto.c", + "nss/lib/ckfw/find.c", + "nss/lib/ckfw/hash.c", + "nss/lib/ckfw/instance.c", + "nss/lib/ckfw/mechanism.c", + "nss/lib/ckfw/mutex.c", + "nss/lib/ckfw/nssck.api", + "nss/lib/ckfw/nssckepv.h", + "nss/lib/ckfw/nssckft.h", + "nss/lib/ckfw/nssckfw.h", + "nss/lib/ckfw/nssckfwc.h", + "nss/lib/ckfw/nssckfwt.h", + "nss/lib/ckfw/nssckg.h", + "nss/lib/ckfw/nssckmdt.h", + "nss/lib/ckfw/nssckt.h", + "nss/lib/ckfw/object.c", + "nss/lib/ckfw/session.c", + "nss/lib/ckfw/sessobj.c", + "nss/lib/ckfw/slot.c", + "nss/lib/ckfw/token.c", + "nss/lib/ckfw/wrap.c", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + + if (is_win) { + configs -= [ "//build/config/win:unicode" ] # Requires 8-bit mode. + } + configs += [ "//build/config/compiler:no_chromium_code" ] + + include_dirs = [ "nss/lib/ckfw" ] + public_configs = [ ":nssckbi_config" ] + + public_deps = [ + ":nss_static", + ] + } + + config("nss_static_config") { + defines = [ + "NSS_STATIC", + "NSS_USE_STATIC_LIBS", + "USE_UTIL_DIRECTLY", + ] + if (is_win) { + defines += [ "_WINDOWS" ] + } + include_dirs = [ + "nspr/pr/include", + "nspr/lib/ds", + "nspr/lib/libc/include", + "nss/lib/base", + "nss/lib/certdb", + "nss/lib/certhigh", + "nss/lib/cryptohi", + "nss/lib/dev", + "nss/lib/freebl", + "nss/lib/freebl/ecl", + "nss/lib/nss", + "nss/lib/pk11wrap", + "nss/lib/pkcs7", + "nss/lib/pki", + "nss/lib/smime", + "nss/lib/softoken", + "nss/lib/util", + ] + } + + if (is_win && current_cpu == "x86") { + source_set("nss_static_avx") { + sources = [ + "nss/lib/freebl/intel-gcm-wrap.c", + "nss/lib/freebl/intel-gcm-x86-masm.asm", + "nss/lib/freebl/intel-gcm.h", + ] + defines = [ + "_WINDOWS", + "_X86_", + "INTEL_GCM", + "MP_API_COMPATIBLE", + "MP_ASSEMBLY_DIV_2DX1D", + "MP_ASSEMBLY_MULTIPLY", + "MP_ASSEMBLY_SQUARE", + "MP_NO_MP_WORD", + "MP_USE_UINT_DIGIT", + "NSS_DISABLE_DBM", + "NSS_STATIC", + "NSS_USE_STATIC_LIBS", + "NSS_X86", + "NSS_X86_OR_X64", + "RIJNDAEL_INCLUDE_TABLES", + "SHLIB_PREFIX=\"\"", + "SHLIB_SUFFIX=\"dll\"", + "SHLIB_VERSION=\"3\"", + "SOFTOKEN_LIB_NAME=\"softokn3.dll\"", + "SOFTOKEN_SHLIB_VERSION=\"3\"", + "USE_HW_AES", + "USE_UTIL_DIRECTLY", + "WIN32", + "WIN95", + "XP_PC", + ] + include_dirs = [ + "nspr/pr/include", + "nspr/lib/ds", + "nspr/lib/libc/include", + "nss/lib/freebl/ecl", + "nss/lib/util", + ] + } + } + + source_set("nss_static") { + visibility = [ ":*" ] # Internal implementation detail. + + sources = [ + "nss/lib/base/arena.c", + "nss/lib/base/base.h", + "nss/lib/base/baset.h", + "nss/lib/base/error.c", + "nss/lib/base/errorval.c", + "nss/lib/base/hash.c", + "nss/lib/base/hashops.c", + "nss/lib/base/item.c", + "nss/lib/base/libc.c", + "nss/lib/base/list.c", + "nss/lib/base/nssbase.h", + "nss/lib/base/nssbaset.h", + "nss/lib/base/nssutf8.c", + "nss/lib/base/tracker.c", + "nss/lib/certdb/alg1485.c", + "nss/lib/certdb/cert.h", + "nss/lib/certdb/certdb.c", + "nss/lib/certdb/certdb.h", + "nss/lib/certdb/certi.h", + "nss/lib/certdb/certt.h", + "nss/lib/certdb/certv3.c", + "nss/lib/certdb/certxutl.c", + "nss/lib/certdb/certxutl.h", + "nss/lib/certdb/crl.c", + "nss/lib/certdb/genname.c", + "nss/lib/certdb/genname.h", + "nss/lib/certdb/polcyxtn.c", + "nss/lib/certdb/secname.c", + "nss/lib/certdb/stanpcertdb.c", + "nss/lib/certdb/xauthkid.c", + "nss/lib/certdb/xbsconst.c", + "nss/lib/certdb/xconst.c", + "nss/lib/certdb/xconst.h", + "nss/lib/certhigh/certhigh.c", + "nss/lib/certhigh/certhtml.c", + "nss/lib/certhigh/certreq.c", + "nss/lib/certhigh/certvfy.c", + "nss/lib/certhigh/crlv2.c", + "nss/lib/certhigh/ocsp.c", + "nss/lib/certhigh/ocsp.h", + "nss/lib/certhigh/ocspi.h", + "nss/lib/certhigh/ocspsig.c", + "nss/lib/certhigh/ocspt.h", + "nss/lib/certhigh/ocspti.h", + "nss/lib/certhigh/xcrldist.c", + "nss/lib/cryptohi/cryptohi.h", + "nss/lib/cryptohi/cryptoht.h", + "nss/lib/cryptohi/dsautil.c", + "nss/lib/cryptohi/key.h", + "nss/lib/cryptohi/keyhi.h", + "nss/lib/cryptohi/keyi.h", + "nss/lib/cryptohi/keyt.h", + "nss/lib/cryptohi/keythi.h", + "nss/lib/cryptohi/sechash.c", + "nss/lib/cryptohi/sechash.h", + "nss/lib/cryptohi/seckey.c", + "nss/lib/cryptohi/secsign.c", + "nss/lib/cryptohi/secvfy.c", + "nss/lib/dev/ckhelper.c", + "nss/lib/dev/ckhelper.h", + "nss/lib/dev/dev.h", + "nss/lib/dev/devm.h", + "nss/lib/dev/devslot.c", + "nss/lib/dev/devt.h", + "nss/lib/dev/devtm.h", + "nss/lib/dev/devtoken.c", + "nss/lib/dev/devutil.c", + "nss/lib/dev/nssdev.h", + "nss/lib/dev/nssdevt.h", + "nss/lib/freebl/aeskeywrap.c", + "nss/lib/freebl/alg2268.c", + "nss/lib/freebl/alghmac.c", + "nss/lib/freebl/alghmac.h", + "nss/lib/freebl/arcfive.c", + "nss/lib/freebl/arcfour.c", + "nss/lib/freebl/blapi.h", + "nss/lib/freebl/blapii.h", + "nss/lib/freebl/blapit.h", + "nss/lib/freebl/camellia.c", + "nss/lib/freebl/camellia.h", + "nss/lib/freebl/chacha20/chacha20.c", + "nss/lib/freebl/chacha20/chacha20.h", + "nss/lib/freebl/chacha20/chacha20_vec.c", + "nss/lib/freebl/chacha20poly1305.c", + "nss/lib/freebl/chacha20poly1305.h", + "nss/lib/freebl/ctr.c", + "nss/lib/freebl/ctr.h", + "nss/lib/freebl/cts.c", + "nss/lib/freebl/cts.h", + "nss/lib/freebl/des.c", + "nss/lib/freebl/des.h", + "nss/lib/freebl/desblapi.c", + "nss/lib/freebl/dh.c", + "nss/lib/freebl/drbg.c", + "nss/lib/freebl/dsa.c", + "nss/lib/freebl/ec.c", + "nss/lib/freebl/ec.h", + "nss/lib/freebl/ecdecode.c", + "nss/lib/freebl/ecl/ec2.h", + "nss/lib/freebl/ecl/ec_naf.c", + "nss/lib/freebl/ecl/ecl-curve.h", + "nss/lib/freebl/ecl/ecl-exp.h", + "nss/lib/freebl/ecl/ecl-priv.h", + "nss/lib/freebl/ecl/ecl.c", + "nss/lib/freebl/ecl/ecl.h", + "nss/lib/freebl/ecl/ecl_curve.c", + "nss/lib/freebl/ecl/ecl_gf.c", + "nss/lib/freebl/ecl/ecl_mult.c", + "nss/lib/freebl/ecl/ecp.h", + "nss/lib/freebl/ecl/ecp_256.c", + "nss/lib/freebl/ecl/ecp_256_32.c", + "nss/lib/freebl/ecl/ecp_384.c", + "nss/lib/freebl/ecl/ecp_521.c", + "nss/lib/freebl/ecl/ecp_aff.c", + "nss/lib/freebl/ecl/ecp_jac.c", + "nss/lib/freebl/ecl/ecp_jm.c", + "nss/lib/freebl/ecl/ecp_mont.c", + "nss/lib/freebl/gcm.c", + "nss/lib/freebl/gcm.h", + "nss/lib/freebl/hmacct.c", + "nss/lib/freebl/hmacct.h", + "nss/lib/freebl/intel-aes-x86-masm.asm", + "nss/lib/freebl/intel-aes.h", + "nss/lib/freebl/jpake.c", + "nss/lib/freebl/md2.c", + "nss/lib/freebl/md5.c", + "nss/lib/freebl/mpi/logtab.h", + "nss/lib/freebl/mpi/mp_gf2m-priv.h", + "nss/lib/freebl/mpi/mp_gf2m.c", + "nss/lib/freebl/mpi/mp_gf2m.h", + "nss/lib/freebl/mpi/mpcpucache.c", + "nss/lib/freebl/mpi/mpi-config.h", + "nss/lib/freebl/mpi/mpi-priv.h", + "nss/lib/freebl/mpi/mpi.c", + "nss/lib/freebl/mpi/mpi.h", + "nss/lib/freebl/mpi/mpi_amd64.c", + "nss/lib/freebl/mpi/mpi_arm.c", + "nss/lib/freebl/mpi/mpi_arm_mac.c", + "nss/lib/freebl/mpi/mpi_x86_asm.c", + "nss/lib/freebl/mpi/mplogic.c", + "nss/lib/freebl/mpi/mplogic.h", + "nss/lib/freebl/mpi/mpmontg.c", + "nss/lib/freebl/mpi/mpprime.c", + "nss/lib/freebl/mpi/mpprime.h", + "nss/lib/freebl/mpi/primes.c", + "nss/lib/freebl/nss_build_config_mac.h", + "nss/lib/freebl/poly1305/poly1305-donna-x64-sse2-incremental-source.c", + "nss/lib/freebl/poly1305/poly1305.c", + "nss/lib/freebl/poly1305/poly1305.h", + "nss/lib/freebl/pqg.c", + "nss/lib/freebl/pqg.h", + "nss/lib/freebl/rawhash.c", + "nss/lib/freebl/rijndael.c", + "nss/lib/freebl/rijndael.h", + "nss/lib/freebl/rijndael32.tab", + "nss/lib/freebl/rsa.c", + "nss/lib/freebl/rsapkcs.c", + "nss/lib/freebl/secmpi.h", + "nss/lib/freebl/secrng.h", + "nss/lib/freebl/seed.c", + "nss/lib/freebl/seed.h", + "nss/lib/freebl/sha256.h", + "nss/lib/freebl/sha512.c", + "nss/lib/freebl/sha_fast.c", + "nss/lib/freebl/sha_fast.h", + "nss/lib/freebl/shsign.h", + "nss/lib/freebl/shvfy.c", + "nss/lib/freebl/sysrand.c", + "nss/lib/freebl/tlsprfalg.c", + "nss/lib/freebl/unix_rand.c", + "nss/lib/freebl/win_rand.c", + "nss/lib/nss/nss.h", + "nss/lib/nss/nssinit.c", + "nss/lib/nss/nssrenam.h", + "nss/lib/nss/utilwrap.c", + "nss/lib/pk11wrap/debug_module.c", + "nss/lib/pk11wrap/dev3hack.c", + "nss/lib/pk11wrap/dev3hack.h", + "nss/lib/pk11wrap/pk11akey.c", + "nss/lib/pk11wrap/pk11auth.c", + "nss/lib/pk11wrap/pk11cert.c", + "nss/lib/pk11wrap/pk11cxt.c", + "nss/lib/pk11wrap/pk11err.c", + "nss/lib/pk11wrap/pk11func.h", + "nss/lib/pk11wrap/pk11kea.c", + "nss/lib/pk11wrap/pk11list.c", + "nss/lib/pk11wrap/pk11load.c", + "nss/lib/pk11wrap/pk11mech.c", + "nss/lib/pk11wrap/pk11merge.c", + "nss/lib/pk11wrap/pk11nobj.c", + "nss/lib/pk11wrap/pk11obj.c", + "nss/lib/pk11wrap/pk11pars.c", + "nss/lib/pk11wrap/pk11pbe.c", + "nss/lib/pk11wrap/pk11pk12.c", + "nss/lib/pk11wrap/pk11pqg.c", + "nss/lib/pk11wrap/pk11pqg.h", + "nss/lib/pk11wrap/pk11priv.h", + "nss/lib/pk11wrap/pk11pub.h", + "nss/lib/pk11wrap/pk11sdr.c", + "nss/lib/pk11wrap/pk11sdr.h", + "nss/lib/pk11wrap/pk11skey.c", + "nss/lib/pk11wrap/pk11slot.c", + "nss/lib/pk11wrap/pk11util.c", + "nss/lib/pk11wrap/secmod.h", + "nss/lib/pk11wrap/secmodi.h", + "nss/lib/pk11wrap/secmodt.h", + "nss/lib/pk11wrap/secmodti.h", + "nss/lib/pk11wrap/secpkcs5.h", + "nss/lib/pkcs7/certread.c", + "nss/lib/pkcs7/p7common.c", + "nss/lib/pkcs7/p7create.c", + "nss/lib/pkcs7/p7decode.c", + "nss/lib/pkcs7/p7encode.c", + "nss/lib/pkcs7/p7local.c", + "nss/lib/pkcs7/p7local.h", + "nss/lib/pkcs7/pkcs7t.h", + "nss/lib/pkcs7/secmime.c", + "nss/lib/pkcs7/secmime.h", + "nss/lib/pkcs7/secpkcs7.h", + "nss/lib/pki/asymmkey.c", + "nss/lib/pki/certdecode.c", + "nss/lib/pki/certificate.c", + "nss/lib/pki/cryptocontext.c", + "nss/lib/pki/nsspki.h", + "nss/lib/pki/nsspkit.h", + "nss/lib/pki/pki.h", + "nss/lib/pki/pki3hack.c", + "nss/lib/pki/pki3hack.h", + "nss/lib/pki/pkibase.c", + "nss/lib/pki/pkim.h", + "nss/lib/pki/pkistore.c", + "nss/lib/pki/pkistore.h", + "nss/lib/pki/pkit.h", + "nss/lib/pki/pkitm.h", + "nss/lib/pki/symmkey.c", + "nss/lib/pki/tdcache.c", + "nss/lib/pki/trustdomain.c", + "nss/lib/smime/cms.h", + "nss/lib/smime/cmslocal.h", + "nss/lib/smime/cmsreclist.h", + "nss/lib/smime/cmst.h", + "nss/lib/smime/smime.h", + "nss/lib/softoken/fipsaudt.c", + "nss/lib/softoken/fipstest.c", + "nss/lib/softoken/fipstokn.c", + "nss/lib/softoken/jpakesftk.c", + "nss/lib/softoken/lgglue.c", + "nss/lib/softoken/lgglue.h", + "nss/lib/softoken/lowkey.c", + "nss/lib/softoken/lowkeyi.h", + "nss/lib/softoken/lowkeyti.h", + "nss/lib/softoken/lowpbe.c", + "nss/lib/softoken/lowpbe.h", + "nss/lib/softoken/padbuf.c", + "nss/lib/softoken/pkcs11.c", + "nss/lib/softoken/pkcs11c.c", + "nss/lib/softoken/pkcs11i.h", + "nss/lib/softoken/pkcs11ni.h", + "nss/lib/softoken/pkcs11u.c", + "nss/lib/softoken/sdb.c", + "nss/lib/softoken/sdb.h", + "nss/lib/softoken/sftkdb.c", + "nss/lib/softoken/sftkdb.h", + "nss/lib/softoken/sftkdbt.h", + "nss/lib/softoken/sftkdbti.h", + "nss/lib/softoken/sftkhmac.c", + "nss/lib/softoken/sftkpars.c", + "nss/lib/softoken/sftkpars.h", + "nss/lib/softoken/sftkpwd.c", + "nss/lib/softoken/softkver.c", + "nss/lib/softoken/softkver.h", + "nss/lib/softoken/softoken.h", + "nss/lib/softoken/softoknt.h", + "nss/lib/softoken/tlsprf.c", + "nss/lib/ssl/sslerr.h", + "nss/lib/util/SECerrs.h", + "nss/lib/util/base64.h", + "nss/lib/util/ciferfam.h", + "nss/lib/util/derdec.c", + "nss/lib/util/derenc.c", + "nss/lib/util/dersubr.c", + "nss/lib/util/dertime.c", + "nss/lib/util/errstrs.c", + "nss/lib/util/hasht.h", + "nss/lib/util/nssb64.h", + "nss/lib/util/nssb64d.c", + "nss/lib/util/nssb64e.c", + "nss/lib/util/nssb64t.h", + "nss/lib/util/nssilckt.h", + "nss/lib/util/nssilock.c", + "nss/lib/util/nssilock.h", + "nss/lib/util/nsslocks.h", + "nss/lib/util/nssrwlk.c", + "nss/lib/util/nssrwlk.h", + "nss/lib/util/nssrwlkt.h", + "nss/lib/util/nssutil.h", + "nss/lib/util/oidstring.c", + "nss/lib/util/pkcs11.h", + "nss/lib/util/pkcs11f.h", + "nss/lib/util/pkcs11n.h", + "nss/lib/util/pkcs11p.h", + "nss/lib/util/pkcs11t.h", + "nss/lib/util/pkcs11u.h", + "nss/lib/util/pkcs1sig.c", + "nss/lib/util/pkcs1sig.h", + "nss/lib/util/portreg.c", + "nss/lib/util/portreg.h", + "nss/lib/util/quickder.c", + "nss/lib/util/secalgid.c", + "nss/lib/util/secasn1.h", + "nss/lib/util/secasn1d.c", + "nss/lib/util/secasn1e.c", + "nss/lib/util/secasn1t.h", + "nss/lib/util/secasn1u.c", + "nss/lib/util/seccomon.h", + "nss/lib/util/secder.h", + "nss/lib/util/secdert.h", + "nss/lib/util/secdig.c", + "nss/lib/util/secdig.h", + "nss/lib/util/secdigt.h", + "nss/lib/util/secerr.h", + "nss/lib/util/secitem.c", + "nss/lib/util/secitem.h", + "nss/lib/util/secoid.c", + "nss/lib/util/secoid.h", + "nss/lib/util/secoidt.h", + "nss/lib/util/secport.c", + "nss/lib/util/secport.h", + "nss/lib/util/sectime.c", + "nss/lib/util/templates.c", + "nss/lib/util/utf8.c", + "nss/lib/util/utilmod.c", + "nss/lib/util/utilmodt.h", + "nss/lib/util/utilpars.c", + "nss/lib/util/utilpars.h", + "nss/lib/util/utilparst.h", + "nss/lib/util/utilrename.h", + ] + + sources -= [ + # mpi_arm.c is included by mpi_arm_mac.c. + # NOTE: mpi_arm.c can be used directly on Linux. mpi_arm.c will need + # to be excluded conditionally if we start to build NSS on Linux. + "nss/lib/freebl/mpi/mpi_arm.c", + + # primes.c is included by mpprime.c. + "nss/lib/freebl/mpi/primes.c", + + # unix_rand.c and win_rand.c are included by sysrand.c. + "nss/lib/freebl/unix_rand.c", + "nss/lib/freebl/win_rand.c", + + # debug_module.c is included by pk11load.c. + "nss/lib/pk11wrap/debug_module.c", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + if (is_win) { + configs -= [ "//build/config/win:unicode" ] # Requires 8-bit mode. + } + configs += [ + "//build/config/compiler:no_chromium_code", + "//build/config/compiler:no_size_t_to_int_warning", + ] + public_configs = [ ":nss_static_config" ] + + cflags = [] + + # Only need the defines and includes not in nss_static_config. + defines = [ + "MP_API_COMPATIBLE", + "NSS_DISABLE_DBM", + "RIJNDAEL_INCLUDE_TABLES", + "SHLIB_VERSION=\"3\"", + "SOFTOKEN_SHLIB_VERSION=\"3\"", + ] + include_dirs = [ + "nss/lib/freebl/mpi", + "nss/lib/ssl", + ] + + if (is_win) { + cflags += [ "/wd4101" ] # Unreferenced local variable. + } + + if (include_nss_libpkix) { + sources += [ + "nss/lib/certhigh/certvfypkix.c", + "nss/lib/certhigh/certvfypkixprint.c", + "nss/lib/libpkix/include/pkix.h", + "nss/lib/libpkix/include/pkix_certsel.h", + "nss/lib/libpkix/include/pkix_certstore.h", + "nss/lib/libpkix/include/pkix_checker.h", + "nss/lib/libpkix/include/pkix_crlsel.h", + "nss/lib/libpkix/include/pkix_errorstrings.h", + "nss/lib/libpkix/include/pkix_params.h", + "nss/lib/libpkix/include/pkix_pl_pki.h", + "nss/lib/libpkix/include/pkix_pl_system.h", + "nss/lib/libpkix/include/pkix_results.h", + "nss/lib/libpkix/include/pkix_revchecker.h", + "nss/lib/libpkix/include/pkix_sample_modules.h", + "nss/lib/libpkix/include/pkix_util.h", + "nss/lib/libpkix/include/pkixt.h", + "nss/lib/libpkix/pkix/certsel/pkix_certselector.c", + "nss/lib/libpkix/pkix/certsel/pkix_certselector.h", + "nss/lib/libpkix/pkix/certsel/pkix_comcertselparams.c", + "nss/lib/libpkix/pkix/certsel/pkix_comcertselparams.h", + "nss/lib/libpkix/pkix/checker/pkix_basicconstraintschecker.c", + "nss/lib/libpkix/pkix/checker/pkix_basicconstraintschecker.h", + "nss/lib/libpkix/pkix/checker/pkix_certchainchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_certchainchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_crlchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_crlchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_ekuchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_ekuchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_expirationchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_expirationchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_namechainingchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_namechainingchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_nameconstraintschecker.c", + "nss/lib/libpkix/pkix/checker/pkix_nameconstraintschecker.h", + "nss/lib/libpkix/pkix/checker/pkix_ocspchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_ocspchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_policychecker.c", + "nss/lib/libpkix/pkix/checker/pkix_policychecker.h", + "nss/lib/libpkix/pkix/checker/pkix_revocationchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_revocationchecker.h", + "nss/lib/libpkix/pkix/checker/pkix_revocationmethod.c", + "nss/lib/libpkix/pkix/checker/pkix_revocationmethod.h", + "nss/lib/libpkix/pkix/checker/pkix_signaturechecker.c", + "nss/lib/libpkix/pkix/checker/pkix_signaturechecker.h", + "nss/lib/libpkix/pkix/checker/pkix_targetcertchecker.c", + "nss/lib/libpkix/pkix/checker/pkix_targetcertchecker.h", + "nss/lib/libpkix/pkix/crlsel/pkix_comcrlselparams.c", + "nss/lib/libpkix/pkix/crlsel/pkix_comcrlselparams.h", + "nss/lib/libpkix/pkix/crlsel/pkix_crlselector.c", + "nss/lib/libpkix/pkix/crlsel/pkix_crlselector.h", + "nss/lib/libpkix/pkix/params/pkix_procparams.c", + "nss/lib/libpkix/pkix/params/pkix_procparams.h", + "nss/lib/libpkix/pkix/params/pkix_resourcelimits.c", + "nss/lib/libpkix/pkix/params/pkix_resourcelimits.h", + "nss/lib/libpkix/pkix/params/pkix_trustanchor.c", + "nss/lib/libpkix/pkix/params/pkix_trustanchor.h", + "nss/lib/libpkix/pkix/params/pkix_valparams.c", + "nss/lib/libpkix/pkix/params/pkix_valparams.h", + "nss/lib/libpkix/pkix/results/pkix_buildresult.c", + "nss/lib/libpkix/pkix/results/pkix_buildresult.h", + "nss/lib/libpkix/pkix/results/pkix_policynode.c", + "nss/lib/libpkix/pkix/results/pkix_policynode.h", + "nss/lib/libpkix/pkix/results/pkix_valresult.c", + "nss/lib/libpkix/pkix/results/pkix_valresult.h", + "nss/lib/libpkix/pkix/results/pkix_verifynode.c", + "nss/lib/libpkix/pkix/results/pkix_verifynode.h", + "nss/lib/libpkix/pkix/store/pkix_store.c", + "nss/lib/libpkix/pkix/store/pkix_store.h", + "nss/lib/libpkix/pkix/top/pkix_build.c", + "nss/lib/libpkix/pkix/top/pkix_build.h", + "nss/lib/libpkix/pkix/top/pkix_lifecycle.c", + "nss/lib/libpkix/pkix/top/pkix_lifecycle.h", + "nss/lib/libpkix/pkix/top/pkix_validate.c", + "nss/lib/libpkix/pkix/top/pkix_validate.h", + "nss/lib/libpkix/pkix/util/pkix_error.c", + "nss/lib/libpkix/pkix/util/pkix_error.h", + "nss/lib/libpkix/pkix/util/pkix_errpaths.c", + "nss/lib/libpkix/pkix/util/pkix_list.c", + "nss/lib/libpkix/pkix/util/pkix_list.h", + "nss/lib/libpkix/pkix/util/pkix_logger.c", + "nss/lib/libpkix/pkix/util/pkix_logger.h", + "nss/lib/libpkix/pkix/util/pkix_tools.c", + "nss/lib/libpkix/pkix/util/pkix_tools.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_aiamgr.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_aiamgr.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_colcertstore.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_colcertstore.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpcertstore.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpcertstore.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpdefaultclient.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_httpdefaultclient.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_nsscontext.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_nsscontext.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_pk11certstore.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_pk11certstore.h", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_socket.c", + "nss/lib/libpkix/pkix_pl_nss/module/pkix_pl_socket.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_basicconstraints.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_basicconstraints.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyinfo.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyinfo.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicymap.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicymap.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyqualifier.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_certpolicyqualifier.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crl.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crl.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crldp.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crldp.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crlentry.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_crlentry.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_date.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_date.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_generalname.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_generalname.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_infoaccess.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_infoaccess.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_nameconstraints.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_nameconstraints.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspcertid.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspcertid.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocsprequest.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_ocspresponse.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_publickey.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_publickey.h", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_x500name.c", + "nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_x500name.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bigint.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bigint.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bytearray.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_bytearray.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_common.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_error.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_hashtable.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_hashtable.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_lifecycle.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_lifecycle.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mem.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mem.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_monitorlock.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_monitorlock.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mutex.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_mutex.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_object.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_object.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_oid.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_oid.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_primhash.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_primhash.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_rwlock.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_rwlock.h", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_string.c", + "nss/lib/libpkix/pkix_pl_nss/system/pkix_pl_string.h", + ] + + # Disable the LDAP code in libpkix. + defines += [ "NSS_PKIX_NO_LDAP" ] + + include_dirs += [ + "nss/lib/libpkix/include", + "nss/lib/libpkix/pkix/certsel", + "nss/lib/libpkix/pkix/checker", + "nss/lib/libpkix/pkix/crlsel", + "nss/lib/libpkix/pkix/params", + "nss/lib/libpkix/pkix/results", + "nss/lib/libpkix/pkix/store", + "nss/lib/libpkix/pkix/top", + "nss/lib/libpkix/pkix/util", + "nss/lib/libpkix/pkix_pl_nss/module", + "nss/lib/libpkix/pkix_pl_nss/pki", + "nss/lib/libpkix/pkix_pl_nss/system", + ] + } else { + defines += [ "NSS_DISABLE_LIBPKIX" ] + } + + if (!include_nss_root_certs) { + defines += [ "NSS_DISABLE_ROOT_CERTS" ] + } + + if (current_cpu == "x64" && !is_win) { + sources -= [ + "nss/lib/freebl/chacha20/chacha20.c", + "nss/lib/freebl/poly1305/poly1305.c", + ] + } else { + sources -= [ + "nss/lib/freebl/chacha20/chacha20_vec.c", + "nss/lib/freebl/poly1305/poly1305-donna-x64-sse2-incremental-source.c", + ] + } + + if (is_mac || is_ios) { + sources -= [ "nss/lib/freebl/mpi/mpi_amd64.c" ] + cflags += [ + "-include", + rebase_path("//third_party/nss/nss/lib/freebl/nss_build_config_mac.h", + root_build_dir), + ] + defines += [ + "XP_UNIX", + "DARWIN", + "HAVE_STRERROR", + "HAVE_BSD_FLOCK", + "SHLIB_SUFFIX=\"dylib\"", + "SHLIB_PREFIX=\"lib\"", + "SOFTOKEN_LIB_NAME=\"libsoftokn3.dylib\"", + ] + + configs -= [ "//build/config/gcc:symbol_visibility_hidden" ] + } else { + # Not Mac/iOS. + sources -= [ "nss/lib/freebl/mpi/mpi_arm_mac.c" ] + } + + if (is_win) { + defines += [ + "SHLIB_SUFFIX=\"dll\"", + "SHLIB_PREFIX=\"\"", + "SOFTOKEN_LIB_NAME=\"softokn3.dll\"", + "XP_PC", + "WIN32", + "WIN95", + ] + + if (current_cpu == "x86") { + defines += [ + "NSS_X86_OR_X64", + "NSS_X86", + "_X86_", + "MP_ASSEMBLY_MULTIPLY", + "MP_ASSEMBLY_SQUARE", + "MP_ASSEMBLY_DIV_2DX1D", + "MP_USE_UINT_DIGIT", + "MP_NO_MP_WORD", + "USE_HW_AES", + "INTEL_GCM", + ] + sources -= [ "nss/lib/freebl/mpi/mpi_amd64.c" ] + } else if (current_cpu == "x64") { + sources -= [ + "nss/lib/freebl/intel-aes-x86-masm.asm", + "nss/lib/freebl/mpi/mpi_amd64.c", + "nss/lib/freebl/mpi/mpi_x86_asm.c", + ] + defines += [ + "NSS_USE_64", + "NSS_X86_OR_X64", + "NSS_X64", + "_AMD64_", + "MP_CHAR_STORE_SLOW", + "MP_IS_LITTLE_ENDIAN", + "WIN64", + ] + } + } else { + # Not Windows. + sources -= [ + # mpi_x86_asm.c contains MSVC inline assembly code. + "nss/lib/freebl/mpi/mpi_x86_asm.c", + ] + } + + if (is_clang) { + cflags += [ + # nss doesn"t explicitly cast between different enum types. + "-Wno-conversion", + + # nss passes "const char*" through "void*". + "-Wno-incompatible-pointer-types", + + # nss prefers `a && b || c` over `(a && b) || c`. + "-Wno-logical-op-parentheses", + + # nss doesn"t use exhaustive switches on enums + "-Wno-switch", + + # nss has some `unsigned < 0` checks. + "-Wno-tautological-compare", + ] + } + + public_deps = [ + ":nspr", + ] + deps = [ + ":nspr", + "//third_party/sqlite", + ] + + if (is_win && current_cpu == "x86") { + deps += [ ":nss_static_avx" ] + } + } +} # Windows/Mac/iOS. diff --git a/engine/src/flutter/build/secondary/tools/grit/BUILD.gn b/engine/src/flutter/build/secondary/tools/grit/BUILD.gn new file mode 100644 index 0000000000..660bf1b1eb --- /dev/null +++ b/engine/src/flutter/build/secondary/tools/grit/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright 2014 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. + +# This target creates a stamp file that depends on all the sources in the grit +# directory. By depending on this, a target can force itself to be rebuilt if +# grit itself changes. +action("grit_sources") { + depfile = "$target_out_dir/grit_sources.d" + script = "//build/secondary/tools/grit/stamp_grit_sources.py" + + inputs = [ + "grit.py", + ] + + # Note that we can't call this "grit_sources.stamp" because that file is + # implicitly created by GN for script actions. + outputs = [ + "$target_out_dir/grit_sources.script.stamp", + ] + + args = [ + rebase_path("//tools/grit", root_build_dir), + rebase_path(outputs[0], root_build_dir), + rebase_path(depfile, root_build_dir), + ] +} diff --git a/engine/src/flutter/build/secondary/tools/grit/grit_rule.gni b/engine/src/flutter/build/secondary/tools/grit/grit_rule.gni new file mode 100644 index 0000000000..95a5e2e133 --- /dev/null +++ b/engine/src/flutter/build/secondary/tools/grit/grit_rule.gni @@ -0,0 +1,474 @@ +# Copyright 2014 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. + +# Instantiate grit. This will produce a script target to run grit, and a +# static library that compiles the .cc files. +# +# Parameters +# +# source (required) +# Path to .grd file. +# +# outputs (required) +# List of outputs from grit, relative to the target_gen_dir. If supplied, +# a call to Grit to compute the outputs can be skipped which will make +# GN run faster. Grit will verify at build time that this list is correct +# and will fail if there is a mismatch between the outputs specified by +# the .grd file and the outputs list here. +# +# To get this list, you can look in the .grd file for +# nodes of the processed files. +# +# output_dir (optional) +# Directory for generated files. If you specify this, you will often +# want to specify output_name if the target name is not particularly +# unique, since this can cause files from multiple grit targets to +# overwrite each other. +# +# output_name (optiona) +# Provide an alternate base name for the generated files, like the .d +# files. Normally these are based on the target name and go in the +# output_dir, but if multiple targets with the same name end up in +# the same output_dir, they can collide. +# +# use_qualified_include (optional) +# If set, output_dir is not added to include_dirs. +# +# configs (optional) +# List of additional configs to be applied to the generated target. +# deps (optional) +# inputs (optional) +# List of additional files, required for grit to process source file. +# visibility (optional) +# Normal meaning. +# +# Example +# +# grit("my_resources") { +# # Source and outputs are required. +# source = "myfile.grd" +# outputs = [ +# "foo_strings.h", +# "foo_strings.pak", +# ] +# +# grit_flags = [ "-E", "foo=bar" ] # Optional extra flags. +# # You can also put deps here if the grit source depends on generated +# # files. +# } +import("//build/config/chrome_build.gni") +import("//build/config/crypto.gni") +import("//build/config/features.gni") +import("//build/config/ui.gni") + +grit_defines = [] + +# Mac and iOS want Title Case strings. +use_titlecase_in_grd_files = is_mac || is_ios +if (use_titlecase_in_grd_files) { + grit_defines += [ + "-D", + "use_titlecase", + ] +} + +if (is_chrome_branded) { + grit_defines += [ + "-D", + "_google_chrome", + "-E", + "CHROMIUM_BUILD=google_chrome", + ] +} else { + grit_defines += [ + "-D", + "_chromium", + "-E", + "CHROMIUM_BUILD=chromium", + ] +} + +if (is_chromeos) { + grit_defines += [ + "-D", + "chromeos", + "-D", + "scale_factors=2x", + ] +} + +if (is_desktop_linux) { + grit_defines += [ + "-D", + "desktop_linux", + ] +} + +if (toolkit_views) { + grit_defines += [ + "-D", + "toolkit_views", + ] +} + +if (use_aura) { + grit_defines += [ + "-D", + "use_aura", + ] +} + +if (use_ash) { + grit_defines += [ + "-D", + "use_ash", + ] +} + +if (use_nss_certs) { + grit_defines += [ + "-D", + "use_nss_certs", + ] +} + +if (use_ozone) { + grit_defines += [ + "-D", + "use_ozone", + ] +} + +if (enable_image_loader_extension) { + grit_defines += [ + "-D", + "image_loader_extension", + ] +} + +if (enable_remoting) { + grit_defines += [ + "-D", + "remoting", + ] +} + +if (is_android) { + grit_defines += [ + "-t", + "android", + "-E", + "ANDROID_JAVA_TAGGED_ONLY=true", + ] +} + +if (is_mac || is_ios) { + grit_defines += [ + "-D", + "scale_factors=2x", + ] +} + +if (is_ios) { + grit_defines += [ + "-t", + "ios", + + # iOS uses a whitelist to filter resources. + "-w", + rebase_path("//build/ios/grit_whitelist.txt", root_build_dir), + ] +} + +if (enable_extensions) { + grit_defines += [ + "-D", + "enable_extensions", + ] +} +if (enable_media_router) { + grit_defines += [ + "-D", + "enable_media_router", + ] +} +if (enable_plugins) { + grit_defines += [ + "-D", + "enable_plugins", + ] +} +if (enable_basic_printing || enable_print_preview) { + grit_defines += [ + "-D", + "enable_printing", + ] + if (enable_print_preview) { + grit_defines += [ + "-D", + "enable_print_preview", + ] + } +} +if (enable_themes) { + grit_defines += [ + "-D", + "enable_themes", + ] +} +if (enable_app_list) { + grit_defines += [ + "-D", + "enable_app_list", + ] +} +if (enable_settings_app) { + grit_defines += [ + "-D", + "enable_settings_app", + ] +} +if (enable_google_now) { + grit_defines += [ + "-D", + "enable_google_now", + ] +} + +# Note: use_concatenated_impulse_responses is omitted. It is never used and +# should probably be removed from GYP build. +if (enable_webrtc) { + grit_defines += [ + "-D", + "enable_webrtc", + ] +} +if (enable_hangout_services_extension) { + grit_defines += [ + "-D", + "enable_hangout_services_extension", + ] +} +if (enable_task_manager) { + grit_defines += [ + "-D", + "enable_task_manager", + ] +} +if (enable_notifications) { + grit_defines += [ + "-D", + "enable_notifications", + ] +} +if (enable_wifi_bootstrapping) { + grit_defines += [ + "-D", + "enable_wifi_bootstrapping", + ] +} +if (enable_service_discovery) { + grit_defines += [ + "-D", + "enable_service_discovery", + ] +} +if (mac_views_browser) { + grit_defines += [ + "-D", + "mac_views_browser", + ] +} + +grit_resource_id_file = "//tools/gritsettings/resource_ids" +grit_info_script = "//tools/grit/grit_info.py" + +template("grit") { + assert(defined(invoker.source), + "\"source\" must be defined for the grit template $target_name") + + grit_inputs = [ invoker.source ] + + if (defined(invoker.resource_ids)) { + resource_ids = invoker.resource_ids + } else { + resource_ids = grit_resource_id_file + } + if (resource_ids != "") { + # The script depends on the ID file. Only add this dependency if the ID + # file is specified. + grit_inputs += [ resource_ids ] + } + + if (defined(invoker.output_dir)) { + output_dir = invoker.output_dir + } else { + output_dir = target_gen_dir + } + + if (defined(invoker.output_name)) { + grit_output_name = invoker.output_name + } else { + grit_output_name = target_name + } + + # These are all passed as arguments to the script so have to be relative to + # the build directory. + if (resource_ids != "") { + resource_ids = rebase_path(resource_ids, root_build_dir) + } + rebased_output_dir = rebase_path(output_dir, root_build_dir) + source_path = rebase_path(invoker.source, root_build_dir) + + if (defined(invoker.grit_flags)) { + grit_flags = invoker.grit_flags + } else { + grit_flags = [] # These are optional so default to empty list. + } + + assert_files_flags = [] + + # We want to make sure the declared outputs actually match what Grit is + # writing. We write the list to a file (some of the output lists are long + # enough to not fit on a Windows command line) and ask Grit to verify those + # are the actual outputs at runtime. + asserted_list_file = + "$target_out_dir/${grit_output_name}_expected_outputs.txt" + write_file(asserted_list_file, + rebase_path(invoker.outputs, root_build_dir, output_dir)) + assert_files_flags += [ "--assert-file-list=" + + rebase_path(asserted_list_file, root_build_dir) ] + grit_outputs = + get_path_info(rebase_path(invoker.outputs, ".", output_dir), "abspath") + + # The config and the action below get this visibility son only the generated + # source set can depend on them. The variable "target_name" will get + # overwritten inside the inner classes so we need to compute it here. + target_visibility = [ ":$target_name" ] + + # The current grit setup makes an file in $output_dir/grit/foo.h that + # the source code expects to include via "grit/foo.h". It would be nice to + # change this to including absolute paths relative to the root gen directory + # (like "mycomponent/foo.h"). This config sets up the include path. + grit_config = target_name + "_grit_config" + config(grit_config) { + if (!defined(invoker.use_qualified_include) || + !invoker.use_qualified_include) { + include_dirs = [ output_dir ] + } + visibility = target_visibility + } + + grit_custom_target = target_name + "_grit" + action(grit_custom_target) { + script = "//tools/grit/grit.py" + inputs = grit_inputs + + depfile = "$output_dir/${grit_output_name}_stamp.d" + outputs = [ "${depfile}.stamp" ] + grit_outputs + + args = [ + "-i", + source_path, + "build", + ] + if (resource_ids != "") { + args += [ + "-f", + resource_ids, + ] + } + args += [ + "-o", + rebased_output_dir, + "--depdir", + ".", + "--depfile", + rebase_path(depfile, root_build_dir), + "--write-only-new=1", + "--depend-on-stamp", + ] + grit_defines + + # Add extra defines with -D flags. + if (defined(invoker.defines)) { + foreach(i, invoker.defines) { + args += [ + "-D", + i, + ] + } + } + + args += grit_flags + assert_files_flags + + if (defined(invoker.visibility)) { + # This needs to include both what the invoker specified (since they + # probably include generated headers from this target), as well as the + # generated source set (since there's no guarantee that the visibility + # specified by the invoker includes our target). + # + # Only define visibility at all if the invoker specified it. Otherwise, + # we want to keep the public "no visibility specified" default. + visibility = target_visibility + invoker.visibility + } + + deps = [ + "//tools/grit:grit_sources", + ] + if (defined(invoker.deps)) { + deps += invoker.deps + } + if (defined(invoker.inputs)) { + inputs += invoker.inputs + } + } + + # This is the thing that people actually link with, it must be named the + # same as the argument the template was invoked with. + source_set(target_name) { + # Since we generate a file, we need to be run before the targets that + # depend on us. + sources = grit_outputs + + # Deps set on the template invocation will go on the action that runs + # grit above rather than this library. This target needs to depend on the + # action publicly so other scripts can take the outputs from the grit + # script as inputs. + public_deps = [ + ":$grit_custom_target", + ] + public_configs = [ ":$grit_config" ] + + if (defined(invoker.public_configs)) { + public_configs += invoker.public_configs + } + + if (defined(invoker.configs)) { + configs += invoker.configs + } + + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + output_name = grit_output_name + } +} diff --git a/engine/src/flutter/build/secondary/tools/grit/repack.gni b/engine/src/flutter/build/secondary/tools/grit/repack.gni new file mode 100644 index 0000000000..1030674c62 --- /dev/null +++ b/engine/src/flutter/build/secondary/tools/grit/repack.gni @@ -0,0 +1,47 @@ +# Copyright 2014 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. + +# This file defines a template to invoke grit repack in a consistent manner. +# +# Parameters: +# sources [required] +# List of pak files that need to be combined. +# +# output [required] +# File name (single string) of the output file. +# +# repack_options [optional] +# List of extra arguments to pass. +# +# deps [optional] +# visibility [optional] +# Normal meaning. +template("repack") { + action(target_name) { + assert(defined(invoker.sources), "Need sources for $target_name") + assert(defined(invoker.output), "Need output for $target_name") + + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + + script = "//tools/grit/grit/format/repack.py" + + inputs = invoker.sources + outputs = [ + invoker.output, + ] + + args = [] + if (defined(invoker.repack_options)) { + args += invoker.repack_options + } + args += [ rebase_path(invoker.output, root_build_dir) ] + args += rebase_path(invoker.sources, root_build_dir) + + if (defined(invoker.deps)) { + deps = invoker.deps + } + } +} diff --git a/engine/src/flutter/build/secondary/tools/grit/stamp_grit_sources.py b/engine/src/flutter/build/secondary/tools/grit/stamp_grit_sources.py new file mode 100644 index 0000000000..d43d4b8c7f --- /dev/null +++ b/engine/src/flutter/build/secondary/tools/grit/stamp_grit_sources.py @@ -0,0 +1,55 @@ +# Copyright 2014 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. + +# This script enumerates the files in the given directory, writing an empty +# stamp file and a .d file listing the inputs required to make the stamp. This +# allows us to dynamically depend on the grit sources without enumerating the +# grit directory for every invocation of grit (which is what adding the source +# files to every .grd file's .d file would entail) or shelling out to grit +# synchronously during GN execution to get the list (which would be slow). +# +# Usage: +# stamp_grit_sources.py <.d-file> + +import os +import sys + +def GritSourceFiles(grit_root_dir): + files = [] + for root, _, filenames in os.walk(grit_root_dir): + grit_src = [os.path.join(root, f) for f in filenames + if f.endswith('.py') and not f.endswith('_unittest.py')] + files.extend(grit_src) + files = [f.replace('\\', '/') for f in files] + return sorted(files) + + +def WriteDepFile(dep_file, stamp_file, source_files): + with open(dep_file, "w") as f: + f.write(stamp_file) + f.write(": ") + f.write(' '.join(source_files)) + + +def WriteStampFile(stamp_file): + with open(stamp_file, "w"): + pass + + +def main(argv): + if len(argv) != 4: + print "Error: expecting 3 args." + return 1 + + grit_root_dir = sys.argv[1] + stamp_file = sys.argv[2] + dep_file = sys.argv[3] + + WriteStampFile(stamp_file) + WriteDepFile(dep_file, stamp_file, GritSourceFiles(grit_root_dir)) + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/build/slave/OWNERS b/engine/src/flutter/build/slave/OWNERS new file mode 100644 index 0000000000..f562c929a1 --- /dev/null +++ b/engine/src/flutter/build/slave/OWNERS @@ -0,0 +1,20 @@ +set noparent +agable@chromium.org +agable@google.com +cmp@chromium.org +cmp@google.com +dpranke@chromium.org +iannucci@chromium.org +iannucci@google.com +johnw@chromium.org +johnw@google.com +maruel@chromium.org +maruel@google.com +mmoss@chromium.org +mmoss@google.com +pschmidt@chromium.org +pschmidt@google.com +stip@chromium.org +stip@google.com +szager@chromium.org +szager@google.com diff --git a/engine/src/flutter/build/slave/README b/engine/src/flutter/build/slave/README new file mode 100644 index 0000000000..e3718b2c28 --- /dev/null +++ b/engine/src/flutter/build/slave/README @@ -0,0 +1,8 @@ +This is a directory which contains configuration information for the +buildsystem. + +* Under recipes, the buildsystem should use only this directory as an + entry point into src/. + +* Scripts in this directory must not import from outside this directory or shell + to scripts outside this directory. diff --git a/engine/src/flutter/build/some.gyp b/engine/src/flutter/build/some.gyp new file mode 100644 index 0000000000..44a1dd59eb --- /dev/null +++ b/engine/src/flutter/build/some.gyp @@ -0,0 +1,24 @@ +# Copyright (c) 2011 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. +{ + 'targets': [ + { + 'target_name': 'some', + 'type': 'none', + 'dependencies': [ + # This file is intended to be locally modified. List the targets you use + # regularly. The generated some.sln will contains projects for only + # those targets and the targets they are transitively dependent on. This + # can result in a solution that loads and unloads faster in Visual + # Studio. + # + # Tip: Create a dummy CL to hold your local edits to this file, so they + # don't accidentally get added to another CL that you are editing. + # + # Example: + # '../chrome/chrome.gyp:chrome', + ], + }, + ], +} diff --git a/engine/src/flutter/build/symlink.py b/engine/src/flutter/build/symlink.py new file mode 100755 index 0000000000..aade2f865c --- /dev/null +++ b/engine/src/flutter/build/symlink.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# 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. + +"""Make a symlink and optionally touch a file (to handle dependencies).""" + + +import errno +import optparse +import os.path +import sys + + +def Main(argv): + parser = optparse.OptionParser() + parser.add_option('-f', '--force', action='store_true') + parser.add_option('--touch') + + options, args = parser.parse_args(argv[1:]) + if len(args) < 2: + parser.error('at least two arguments required.') + + target = args[-1] + sources = args[:-1] + for s in sources: + t = os.path.join(target, os.path.basename(s)) + try: + os.symlink(s, t) + except OSError, e: + if e.errno == errno.EEXIST and options.force: + os.remove(t) + os.symlink(s, t) + else: + raise + + + if options.touch: + with open(options.touch, 'w') as f: + pass + + +if __name__ == '__main__': + sys.exit(Main(sys.argv)) diff --git a/engine/src/flutter/build/temp_gyp/README.chromium b/engine/src/flutter/build/temp_gyp/README.chromium new file mode 100644 index 0000000000..8045d61591 --- /dev/null +++ b/engine/src/flutter/build/temp_gyp/README.chromium @@ -0,0 +1,3 @@ +This directory will be removed once the files in it are committed upstream and +Chromium imports an upstream revision with these files. Contact mark for +details. diff --git a/engine/src/flutter/build/temp_gyp/pdfsqueeze.gyp b/engine/src/flutter/build/temp_gyp/pdfsqueeze.gyp new file mode 100644 index 0000000000..2b3b1ff81c --- /dev/null +++ b/engine/src/flutter/build/temp_gyp/pdfsqueeze.gyp @@ -0,0 +1,40 @@ +# Copyright (c) 2009 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. + +{ + 'targets': [ + { + 'target_name': 'pdfsqueeze', + 'type': 'executable', + 'sources': [ + '../../third_party/pdfsqueeze/pdfsqueeze.m', + ], + 'defines': [ + # Use defines to map the full path names that will be used for + # the vars into the short forms expected by pdfsqueeze.m. + '______third_party_pdfsqueeze_ApplyGenericRGB_qfilter=ApplyGenericRGB_qfilter', + '______third_party_pdfsqueeze_ApplyGenericRGB_qfilter_len=ApplyGenericRGB_qfilter_len', + ], + 'include_dirs': [ + '<(INTERMEDIATE_DIR)', + ], + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Foundation.framework', + '$(SDKROOT)/System/Library/Frameworks/Quartz.framework', + ], + 'actions': [ + { + 'action_name': 'Generate inline filter data', + 'inputs': [ + '../../third_party/pdfsqueeze/ApplyGenericRGB.qfilter', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/ApplyGenericRGB.h', + ], + 'action': ['xxd', '-i', '<@(_inputs)', '<@(_outputs)'], + }, + ], + }, + ], +} diff --git a/engine/src/flutter/build/toolchain/OWNERS b/engine/src/flutter/build/toolchain/OWNERS new file mode 100644 index 0000000000..c6cda3fcdd --- /dev/null +++ b/engine/src/flutter/build/toolchain/OWNERS @@ -0,0 +1,3 @@ +brettw@chromium.org +dpranke@chromium.org +scottmg@chromium.org diff --git a/engine/src/flutter/build/toolchain/android/BUILD.gn b/engine/src/flutter/build/toolchain/android/BUILD.gn new file mode 100644 index 0000000000..f7f47ff229 --- /dev/null +++ b/engine/src/flutter/build/toolchain/android/BUILD.gn @@ -0,0 +1,131 @@ +# Copyright 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. + +import("//build/config/sysroot.gni") # Imports android/config.gni. +import("//build/toolchain/ccache.gni") +import("//build/toolchain/clang.gni") +import("//build/toolchain/goma.gni") +import("//build/toolchain/gcc_toolchain.gni") + +# The Android GCC toolchains share most of the same parameters, so we have this +# wrapper around gcc_toolchain to avoid duplication of logic. +# +# Parameters: +# - android_ndk_sysroot +# Sysroot for this architecture. +# - android_ndk_lib_dir +# Subdirectory inside of android_ndk_sysroot where libs go. +# - tool_prefix +# Prefix to be added to the tool names. +# - toolchain_cpu +# Same as gcc_toolchain +template("android_gcc_toolchain") { + gcc_toolchain(target_name) { + # Make our manually injected libs relative to the build dir. + android_ndk_lib = rebase_path( + invoker.android_ndk_sysroot + "/" + invoker.android_ndk_lib_dir, + root_build_dir) + + libs_section_prefix = "$android_ndk_lib/crtbegin_dynamic.o" + libs_section_postfix = "$android_ndk_lib/crtend_android.o" + + solink_libs_section_prefix = "$android_ndk_lib/crtbegin_so.o" + solink_libs_section_postfix = "$android_ndk_lib/crtend_so.o" + + # The tools should be run relative to the build dir. + tool_prefix = rebase_path(invoker.tool_prefix, root_build_dir) + + if (use_goma) { + assert(!use_ccache, "Goma and ccache can't be used together.") + compiler_prefix = "$goma_dir/gomacc " + } else if (use_ccache) { + compiler_prefix = "ccache " + } else { + compiler_prefix = "" + } + + cc = compiler_prefix + tool_prefix + "gcc" + cxx = compiler_prefix + tool_prefix + "g++" + ar = tool_prefix + "ar" + ld = cxx + readelf = compiler_prefix + tool_prefix + "readelf" + nm = compiler_prefix + tool_prefix + "nm" + + toolchain_os = "android" + toolchain_cpu = invoker.toolchain_cpu + + # We make the assumption that the gcc_toolchain will produce a soname with + # the following definition. + soname = "{{target_output_name}}{{output_extension}}" + + stripped_soname = "lib.stripped/${soname}" + temp_stripped_soname = "${stripped_soname}.tmp" + + android_strip = "${tool_prefix}strip" + + mkdir_command = "mkdir -p lib.stripped" + strip_command = + "$android_strip --strip-unneeded -o $temp_stripped_soname $soname" + replace_command = "if ! cmp -s $temp_stripped_soname $stripped_soname; then mv $temp_stripped_soname $stripped_soname; fi" + postsolink = "$mkdir_command && $strip_command && $replace_command" + solink_outputs = [ stripped_soname ] + + # We make the assumption that the gcc_toolchain will produce an exe with + # the following definition. + exe = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" + stripped_exe = "exe.stripped/$exe" + mkdir_command = "mkdir -p exe.stripped" + strip_command = "$android_strip --strip-unneeded -o $stripped_exe $exe" + postlink = "$mkdir_command && $strip_command" + link_outputs = [ stripped_exe ] + } +} + +android_gcc_toolchain("x86") { + android_ndk_sysroot = "$android_ndk_root/$x86_android_sysroot_subdir" + android_ndk_lib_dir = "usr/lib" + + tool_prefix = "$x86_android_toolchain_root/bin/i686-linux-android-" + toolchain_cpu = "x86" +} + +android_gcc_toolchain("arm") { + android_ndk_sysroot = "$android_ndk_root/$arm_android_sysroot_subdir" + android_ndk_lib_dir = "usr/lib" + + tool_prefix = "$arm_android_toolchain_root/bin/arm-linux-androideabi-" + toolchain_cpu = "arm" +} + +android_gcc_toolchain("mipsel") { + android_ndk_sysroot = "$android_ndk_root/$mips_android_sysroot_subdir" + android_ndk_lib_dir = "usr/lib" + + tool_prefix = "$mips_android_toolchain_root/bin/mipsel-linux-android-" + toolchain_cpu = "mipsel" +} + +android_gcc_toolchain("x64") { + android_ndk_sysroot = "$android_ndk_root/$x86_64_android_sysroot_subdir" + android_ndk_lib_dir = "usr/lib64" + + tool_prefix = "$x86_64_android_toolchain_root/bin/x86_64-linux-android-" + toolchain_cpu = "x86_64" +} + +android_gcc_toolchain("arm64") { + android_ndk_sysroot = "$android_ndk_root/$arm64_android_sysroot_subdir" + android_ndk_lib_dir = "usr/lib" + + tool_prefix = "$arm64_android_toolchain_root/bin/arm-linux-androideabi-" + toolchain_cpu = "aarch64" +} + +android_gcc_toolchain("mips64el") { + android_ndk_sysroot = "$android_ndk_root/$mips64_android_sysroot_subdir" + android_ndk_lib_dir = "usr/lib64" + + tool_prefix = "$mips64_android_toolchain_root/bin/mipsel-linux-android-" + toolchain_cpu = "mipsel64el" +} diff --git a/engine/src/flutter/build/toolchain/ccache.gni b/engine/src/flutter/build/toolchain/ccache.gni new file mode 100644 index 0000000000..806e079a4c --- /dev/null +++ b/engine/src/flutter/build/toolchain/ccache.gni @@ -0,0 +1,25 @@ +# Copyright (c) 2014 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. + +# Defines the configuration of ccache - a c/c++ compiler cache which can +# greatly reduce recompilation times. +# +# TIPS: +# +# Set clang_use_chrome_plugins=false if using ccache 3.1.9 or earlier, since +# these versions don't support -Xclang. (3.1.10 and later will silently +# ignore -Xclang, so it doesn't matter if you disable clang_use_chrome_plugins +# or not). +# +# Use ccache 3.2 or later to avoid clang unused argument warnings: +# https://bugzilla.samba.org/show_bug.cgi?id=8118 +# +# To avoid -Wparentheses-equality clang warnings, at some cost in terms of +# speed, you can do: +# export CCACHE_CPP2=yes + +declare_args() { + # Set to true to enable ccache. Probably doesn't work on windows. + use_ccache = false +} diff --git a/engine/src/flutter/build/toolchain/clang.gni b/engine/src/flutter/build/toolchain/clang.gni new file mode 100644 index 0000000000..c6803844d8 --- /dev/null +++ b/engine/src/flutter/build/toolchain/clang.gni @@ -0,0 +1,9 @@ +# 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. + +declare_args() { + # Enable the optional type profiler in Clang, which will tag heap allocations + # with the allocation type. + use_clang_type_profiler = false +} diff --git a/engine/src/flutter/build/toolchain/cros/BUILD.gn b/engine/src/flutter/build/toolchain/cros/BUILD.gn new file mode 100644 index 0000000000..140958b5b9 --- /dev/null +++ b/engine/src/flutter/build/toolchain/cros/BUILD.gn @@ -0,0 +1,35 @@ +# Copyright 2014 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. + +import("//build/toolchain/clang.gni") +import("//build/toolchain/gcc_toolchain.gni") + +declare_args() { + # The CrOS build system supports many different kinds of targets across + # many different architectures. Bringing your own toolchain is also supported, + # so it's actually impossible to enumerate all toolchains for all targets + # as GN toolchain specifications. + # These arguments provide a mechanism for specifying your CC, CXX and AR at + # buildfile-generation time, allowing the CrOS build system to always use + # the right tools for the current target. + cros_target_cc = "" + cros_target_cxx = "" + cros_target_ar = "" +} + +gcc_toolchain("target") { + assert(cros_target_cc != "", "Must provide target CC.") + assert(cros_target_cxx != "", "Must provide target CXX.") + assert(cros_target_ar != "", "Must provide target AR.") + + cc = "${cros_target_cc}" + cxx = "${cros_target_cxx}" + + ar = "${cros_target_ar}" + ld = cxx + + toolchain_cpu = "${target_cpu}" + toolchain_os = "linux" + is_clang = is_clang +} diff --git a/engine/src/flutter/build/toolchain/gcc_toolchain.gni b/engine/src/flutter/build/toolchain/gcc_toolchain.gni new file mode 100644 index 0000000000..ae9599a461 --- /dev/null +++ b/engine/src/flutter/build/toolchain/gcc_toolchain.gni @@ -0,0 +1,230 @@ +# 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. + +# This value will be inherited in the toolchain below. +concurrent_links = exec_script("get_concurrent_links.py", [], "value") + +# This template defines a toolchain for something that works like gcc +# (including clang). +# +# It requires the following variables specifying the executables to run: +# - cc +# - cxx +# - ar +# - ld +# - readelf +# - nm +# and the following which is used in the toolchain_args +# - toolchain_cpu (What "current_cpu" should be set to when invoking a +# build using this toolchain.) +# - toolchain_os (What "current_os" should be set to when invoking a +# build using this toolchain.) +# +# Optional parameters: +# - libs_section_prefix +# - libs_section_postfix +# The contents of these strings, if specified, will be placed around +# the libs section of the linker line. It allows one to inject libraries +# at the beginning and end for all targets in a toolchain. +# - solink_libs_section_prefix +# - solink_libs_section_postfix +# Same as libs_section_{pre,post}fix except used for solink instead of link. +# - post_solink +# The content of this string, if specified, will be appended to the solink +# command. +# - deps +# Just forwarded to the toolchain definition. +# - is_clang +template("gcc_toolchain") { + toolchain(target_name) { + assert(defined(invoker.cc), "gcc_toolchain() must specify a \"cc\" value") + assert(defined(invoker.cxx), "gcc_toolchain() must specify a \"cxx\" value") + assert(defined(invoker.ar), "gcc_toolchain() must specify a \"ar\" value") + assert(defined(invoker.ld), "gcc_toolchain() must specify a \"ld\" value") + assert(defined(invoker.readelf), + "gcc_toolchain() must specify a \"readelf\" value") + assert(defined(invoker.nm), "gcc_toolchain() must specify a \"nm\" value") + assert(defined(invoker.toolchain_cpu), + "gcc_toolchain() must specify a \"toolchain_cpu\"") + assert(defined(invoker.toolchain_os), + "gcc_toolchain() must specify a \"toolchain_os\"") + + # We can't do string interpolation ($ in strings) on things with dots in + # them. To allow us to use $cc below, for example, we create copies of + # these values in our scope. + cc = invoker.cc + cxx = invoker.cxx + ar = invoker.ar + ld = invoker.ld + readelf = invoker.readelf + nm = invoker.nm + + # Bring these into our scope for string interpolation with default values. + if (defined(invoker.libs_section_prefix)) { + libs_section_prefix = invoker.libs_section_prefix + } else { + libs_section_prefix = "" + } + + if (defined(invoker.libs_section_postfix)) { + libs_section_postfix = invoker.libs_section_postfix + } else { + libs_section_postfix = "" + } + + if (defined(invoker.solink_libs_section_prefix)) { + solink_libs_section_prefix = invoker.solink_libs_section_prefix + } else { + solink_libs_section_prefix = "" + } + + if (defined(invoker.solink_libs_section_postfix)) { + solink_libs_section_postfix = invoker.solink_libs_section_postfix + } else { + solink_libs_section_postfix = "" + } + + # These library switches can apply to all tools below. + lib_switch = "-l" + lib_dir_switch = "-L" + + tool("cc") { + depfile = "{{output}}.d" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CC {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o", + ] + } + + tool("cxx") { + depfile = "{{output}}.d" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CXX {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o", + ] + } + + tool("asm") { + # For GCC we can just use the C compiler to compile assembly. + depfile = "{{output}}.d" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "ASM {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o", + ] + } + + tool("alink") { + rspfile = "{{output}}.rsp" + command = "rm -f {{output}} && $ar rcs {{output}} @$rspfile" + description = "AR {{output}}" + rspfile_content = "{{inputs}}" + outputs = [ + "{{target_out_dir}}/{{target_output_name}}{{output_extension}}", + ] + default_output_extension = ".a" + output_prefix = "lib" + } + + tool("solink") { + soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so". + sofile = "{{root_out_dir}}/$soname" # Possibly including toolchain dir. + rspfile = sofile + ".rsp" + + # These variables are not built into GN but are helpers that implement + # (1) linking to produce a .so, (2) extracting the symbols from that file + # to a temporary file, (3) if the temporary file has differences from the + # existing .TOC file, overwrite it, otherwise, don't change it. + tocfile = sofile + ".TOC" + temporary_tocname = sofile + ".tmp" + link_command = + "$ld -shared {{ldflags}} -o $sofile -Wl,-soname=$soname @$rspfile" + toc_command = "{ $readelf -d $sofile | grep SONAME ; $nm -gD -f p $sofile | cut -f1-2 -d' '; } > $temporary_tocname" + replace_command = "if ! cmp -s $temporary_tocname $tocfile; then mv $temporary_tocname $tocfile; fi" + + command = "$link_command && $toc_command && $replace_command" + if (defined(invoker.postsolink)) { + command += " && " + invoker.postsolink + } + rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive $solink_libs_section_prefix {{libs}} $solink_libs_section_postfix" + + description = "SOLINK $sofile" + + # Use this for {{output_extension}} expansions unless a target manually + # overrides it (in which case {{output_extension}} will be what the target + # specifies). + default_output_extension = ".so" + + output_prefix = "lib" + + # Since the above commands only updates the .TOC file when it changes, ask + # Ninja to check if the timestamp actually changed to know if downstream + # dependencies should be recompiled. + restat = true + + # Tell GN about the output files. It will link to the sofile but use the + # tocfile for dependency management. + outputs = [ + sofile, + tocfile, + ] + if (defined(invoker.solink_outputs)) { + outputs += invoker.solink_outputs + } + link_output = sofile + depend_output = tocfile + } + + tool("link") { + outfile = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" + rspfile = "$outfile.rsp" + command = "$ld {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group $libs_section_prefix {{libs}} $libs_section_postfix" + if (defined(invoker.postlink)) { + command += " && " + invoker.postlink + } + description = "LINK $outfile" + rspfile_content = "{{inputs}}" + outputs = [ + outfile, + ] + if (defined(invoker.link_outputs)) { + outputs += invoker.link_outputs + } + } + + tool("stamp") { + command = "touch {{output}}" + description = "STAMP {{output}}" + } + + tool("copy") { + command = "ln -f {{source}} {{output}} 2>/dev/null || (rm -rf {{output}} && cp -af {{source}} {{output}})" + description = "COPY {{source}} {{output}}" + } + + # When invoking this toolchain not as the default one, these args will be + # passed to the build. They are ignored when this is the default toolchain. + toolchain_args() { + current_cpu = invoker.toolchain_cpu + current_os = invoker.toolchain_os + + # These values need to be passed through unchanged. + target_os = target_os + target_cpu = target_cpu + + if (defined(invoker.is_clang)) { + is_clang = invoker.is_clang + } + } + + if (defined(invoker.deps)) { + deps = invoker.deps + } + } +} diff --git a/engine/src/flutter/build/toolchain/get_concurrent_links.py b/engine/src/flutter/build/toolchain/get_concurrent_links.py new file mode 100644 index 0000000000..6a401017eb --- /dev/null +++ b/engine/src/flutter/build/toolchain/get_concurrent_links.py @@ -0,0 +1,64 @@ +# Copyright 2014 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. + +# This script computs the number of concurrent links we want to run in the build +# as a function of machine spec. It's based on GetDefaultConcurrentLinks in GYP. + +import os +import re +import subprocess +import sys + +def GetDefaultConcurrentLinks(): + # Inherit the legacy environment variable for people that have set it in GYP. + pool_size = int(os.getenv('GYP_LINK_CONCURRENCY', 0)) + if pool_size: + return pool_size + + if sys.platform in ('win32', 'cygwin'): + import ctypes + + class MEMORYSTATUSEX(ctypes.Structure): + _fields_ = [ + ("dwLength", ctypes.c_ulong), + ("dwMemoryLoad", ctypes.c_ulong), + ("ullTotalPhys", ctypes.c_ulonglong), + ("ullAvailPhys", ctypes.c_ulonglong), + ("ullTotalPageFile", ctypes.c_ulonglong), + ("ullAvailPageFile", ctypes.c_ulonglong), + ("ullTotalVirtual", ctypes.c_ulonglong), + ("ullAvailVirtual", ctypes.c_ulonglong), + ("sullAvailExtendedVirtual", ctypes.c_ulonglong), + ] + + stat = MEMORYSTATUSEX(dwLength=ctypes.sizeof(MEMORYSTATUSEX)) + ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)) + + mem_limit = max(1, stat.ullTotalPhys / (4 * (2 ** 30))) # total / 4GB + hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32))) + return min(mem_limit, hard_cap) + elif sys.platform.startswith('linux'): + if os.path.exists("/proc/meminfo"): + with open("/proc/meminfo") as meminfo: + memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB') + for line in meminfo: + match = memtotal_re.match(line) + if not match: + continue + # Allow 8Gb per link on Linux because Gold is quite memory hungry + return max(1, int(match.group(1)) / (8 * (2 ** 20))) + return 1 + elif sys.platform == 'darwin': + try: + avail_bytes = int(subprocess.check_output(['sysctl', '-n', 'hw.memsize'])) + # A static library debug build of Chromium's unit_tests takes ~2.7GB, so + # 4GB per ld process allows for some more bloat. + return max(1, avail_bytes / (4 * (2 ** 30))) # total / 4GB + except Exception: + return 1 + else: + # TODO(scottmg): Implement this for other platforms. + return 1 + +print GetDefaultConcurrentLinks() diff --git a/engine/src/flutter/build/toolchain/goma.gni b/engine/src/flutter/build/toolchain/goma.gni new file mode 100644 index 0000000000..c0f4cf2825 --- /dev/null +++ b/engine/src/flutter/build/toolchain/goma.gni @@ -0,0 +1,22 @@ +# 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. + +# Defines the configuration of Goma. +# +# This is currently designed to match the GYP build exactly, so as not to break +# people during the transition. + +declare_args() { + # Set to true to enable distributed compilation using Goma. + use_goma = false + + # Set the default value based on the platform. + if (is_win) { + # Absolute directory containing the Goma source code. + goma_dir = "C:\goma\goma-win" + } else { + # Absolute directory containing the Goma source code. + goma_dir = getenv("HOME") + "/goma" + } +} diff --git a/engine/src/flutter/build/toolchain/linux/BUILD.gn b/engine/src/flutter/build/toolchain/linux/BUILD.gn new file mode 100644 index 0000000000..15ad5bdea8 --- /dev/null +++ b/engine/src/flutter/build/toolchain/linux/BUILD.gn @@ -0,0 +1,114 @@ +# Copyright 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. + +import("//build/config/sysroot.gni") +import("//build/toolchain/ccache.gni") +import("//build/toolchain/clang.gni") +import("//build/toolchain/gcc_toolchain.gni") +import("//build/toolchain/goma.gni") + +if (use_goma) { + assert(!use_ccache, "Goma and ccache can't be used together.") + compiler_prefix = "$goma_dir/gomacc " +} else if (use_ccache) { + compiler_prefix = "ccache " +} else { + compiler_prefix = "" +} + +gcc_toolchain("arm") { + cc = "${compiler_prefix}arm-linux-gnueabi-gcc" + cxx = "${compiler_prefix}arm-linux-gnueabi-g++" + + ar = "arm-linux-gnueabi-ar" + ld = cxx + readelf = "arm-linux-gnueabi-readelf" + nm = "arm-linux-gnueabi-nm" + + toolchain_cpu = "arm" + toolchain_os = "linux" + is_clang = false +} + +gcc_toolchain("clang_x86") { + if (use_clang_type_profiler) { + prefix = rebase_path("//third_party/llvm-allocated-type/Linux_ia32/bin", + root_build_dir) + } else { + prefix = rebase_path("//third_party/llvm-build/Release+Asserts/bin", + root_build_dir) + } + cc = "${compiler_prefix}$prefix/clang" + cxx = "${compiler_prefix}$prefix/clang++" + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + toolchain_cpu = "x86" + toolchain_os = "linux" + is_clang = true +} + +gcc_toolchain("x86") { + cc = "${compiler_prefix}gcc" + cxx = "$compiler_prefix}g++" + + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + toolchain_cpu = "x86" + toolchain_os = "linux" + is_clang = false +} + +gcc_toolchain("clang_x64") { + if (use_clang_type_profiler) { + prefix = rebase_path("//third_party/llvm-allocated-type/Linux_x64/bin", + root_build_dir) + } else { + prefix = rebase_path("//third_party/llvm-build/Release+Asserts/bin", + root_build_dir) + } + cc = "${compiler_prefix}$prefix/clang" + cxx = "${compiler_prefix}$prefix/clang++" + + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + toolchain_cpu = "x64" + toolchain_os = "linux" + is_clang = true +} + +gcc_toolchain("x64") { + cc = "${compiler_prefix}gcc" + cxx = "${compiler_prefix}g++" + + readelf = "readelf" + nm = "nm" + ar = "ar" + ld = cxx + + toolchain_cpu = "x64" + toolchain_os = "linux" + is_clang = false +} + +gcc_toolchain("mipsel") { + cc = "mipsel-linux-gnu-gcc" + cxx = "mipsel-linux-gnu-g++" + ar = "mipsel-linux-gnu-ar" + ld = cxx + readelf = "mipsel-linux-gnu-readelf" + nm = "mipsel-linux-gnu-nm" + + toolchain_cpu = "mipsel" + toolchain_os = "linux" + is_clang = false +} diff --git a/engine/src/flutter/build/toolchain/mac/BUILD.gn b/engine/src/flutter/build/toolchain/mac/BUILD.gn new file mode 100644 index 0000000000..da4ca0532e --- /dev/null +++ b/engine/src/flutter/build/toolchain/mac/BUILD.gn @@ -0,0 +1,234 @@ +# 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. + +# TODO(brettw) Use "gcc_toolchain.gni" like the Linux toolchains. This requires +# some enhancements since the commands on Mac are slightly different than on +# Linux. + +import("../goma.gni") + +assert(host_os == "mac") + +import("//build/toolchain/clang.gni") +import("//build/toolchain/goma.gni") + +if (use_goma) { + goma_prefix = "$goma_dir/gomacc " +} else { + goma_prefix = "" +} + +# This will copy the gyp-mac-tool to the build directory. We pass in the source +# file of the win tool. +gyp_mac_tool_source = + rebase_path("//tools/gyp/pylib/gyp/mac_tool.py", root_build_dir) +exec_script("setup_toolchain.py", [ gyp_mac_tool_source ]) + +# Shared toolchain definition. Invocations should set toolchain_os to set the +# build args in this definition. +template("mac_toolchain") { + toolchain(target_name) { + assert(defined(invoker.cc), "mac_toolchain() must specify a \"cc\" value") + assert(defined(invoker.cxx), "mac_toolchain() must specify a \"cxx\" value") + assert(defined(invoker.ld), "mac_toolchain() must specify a \"ld\" value") + assert(defined(invoker.toolchain_cpu), + "mac_toolchain() must specify a \"toolchain_cpu\"") + assert(defined(invoker.toolchain_os), + "mac_toolchain() must specify a \"toolchain_os\"") + + # We can't do string interpolation ($ in strings) on things with dots in + # them. To allow us to use $cc below, for example, we create copies of + # these values in our scope. + cc = invoker.cc + cxx = invoker.cxx + ld = invoker.ld + + # Make these apply to all tools below. + lib_switch = "-l" + lib_dir_switch = "-L" + + tool("cc") { + depfile = "{{output}}.d" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CC {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o", + ] + } + + tool("cxx") { + depfile = "{{output}}.d" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "CXX {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o", + ] + } + + tool("asm") { + # For GCC we can just use the C compiler to compile assembly. + depfile = "{{output}}.d" + command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "ASM {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o", + ] + } + + tool("objc") { + depfile = "{{output}}.d" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} {{cflags_objc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "OBJC {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o", + ] + } + + tool("objcxx") { + depfile = "{{output}}.d" + command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} {{cflags_objcc}} -c {{source}} -o {{output}}" + depsformat = "gcc" + description = "OBJCXX {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o", + ] + } + + tool("alink") { + command = "rm -f {{output}} && ./gyp-mac-tool filter-libtool libtool -static -o {{output}} {{inputs}}" + description = "LIBTOOL-STATIC {{output}}" + outputs = [ + "{{target_out_dir}}/{{target_output_name}}{{output_extension}}", + ] + default_output_extension = ".a" + output_prefix = "lib" + } + + tool("solink") { + dylib = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" # eg "./libfoo.dylib" + rspfile = dylib + ".rsp" + + # These variables are not build into GN but are helpers that implement + # (1) linking to produce a .so, (2) extracting the symbols from that file + # to a temporary file, (3) if the temporary file has differences from the + # existing .TOC file, overwrite it, oterwise, don't change it. + # + # As a special case, if the library reexports symbols from other dynamic + # libraries, we always update the .TOC and skip the temporary file and + # diffing steps, since that library always needs to be re-linked. + tocname = dylib + ".TOC" + temporary_tocname = dylib + ".tmp" + + does_reexport_command = "[ ! -e $dylib -o ! -e $tocname ] || otool -l $dylib | grep -q LC_REEXPORT_DYLIB" + link_command = "$ld -shared {{ldflags}} -o $dylib -Wl,-filelist,$rspfile {{solibs}} {{libs}}" + replace_command = "if ! cmp -s $temporary_tocname $tocname; then mv $temporary_tocname $tocname" + extract_toc_command = "{ otool -l $dylib | grep LC_ID_DYLIB -A 5; nm -gP $dylib | cut -f1-2 -d' ' | grep -v U\$\$; true; }" + + command = "if $does_reexport_command ; then $link_command && $extract_toc_command > $tocname; else $link_command && $extract_toc_command > $temporary_tocname && $replace_command ; fi; fi" + + rspfile_content = "{{inputs_newline}}" + + description = "SOLINK {{output}}" + + # Use this for {{output_extension}} expansions unless a target manually + # overrides it (in which case {{output_extension}} will be what the target + # specifies). + default_output_extension = ".dylib" + + output_prefix = "lib" + + # Since the above commands only updates the .TOC file when it changes, ask + # Ninja to check if the timestamp actually changed to know if downstream + # dependencies should be recompiled. + restat = true + + # Tell GN about the output files. It will link to the dylib but use the + # tocname for dependency management. + outputs = [ + dylib, + tocname, + ] + link_output = dylib + depend_output = tocname + } + + tool("link") { + outfile = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" + rspfile = "$outfile.rsp" + command = "$ld {{ldflags}} -o $outfile -Wl,-filelist,$rspfile {{solibs}} {{libs}}" + description = "LINK $outfile" + rspfile_content = "{{inputs_newline}}" + outputs = [ + outfile, + ] + } + + tool("stamp") { + command = "touch {{output}}" + description = "STAMP {{output}}" + } + + tool("copy") { + command = "ln -f {{source}} {{output}} 2>/dev/null || (rm -rf {{output}} && cp -af {{source}} {{output}})" + description = "COPY {{source}} {{output}}" + } + + toolchain_args() { + current_cpu = invoker.toolchain_cpu + current_os = invoker.toolchain_os + + # These values need to be passed through unchanged. + target_os = target_os + target_cpu = target_cpu + + if (defined(invoker.is_clang)) { + is_clang = invoker.is_clang + } + } + } +} + +mac_toolchain("clang_arm") { + toolchain_cpu = "arm" + toolchain_os = "mac" + prefix = rebase_path("//third_party/llvm-build/Release+Asserts/bin", + root_build_dir) + cc = "${goma_prefix}$prefix/clang" + cxx = "${goma_prefix}$prefix/clang++" + ld = cxx + is_clang = true +} + +mac_toolchain("arm") { + toolchain_cpu = "arm" + toolchain_os = "mac" + cc = "${goma_prefix}/gcc" + cxx = "${goma_prefix}/g++" + ld = cxx + is_clang = false +} + +mac_toolchain("clang_x64") { + toolchain_cpu = "x64" + toolchain_os = "mac" + prefix = rebase_path("//third_party/llvm-build/Release+Asserts/bin", + root_build_dir) + cc = "${goma_prefix}$prefix/clang" + cxx = "${goma_prefix}$prefix/clang++" + ld = cxx + is_clang = true +} + +mac_toolchain("x64") { + toolchain_cpu = "x64" + toolchain_os = "mac" + cc = "${goma_prefix}/gcc" + cxx = "${goma_prefix}/g++" + ld = cxx + is_clang = false +} diff --git a/engine/src/flutter/build/toolchain/mac/setup_toolchain.py b/engine/src/flutter/build/toolchain/mac/setup_toolchain.py new file mode 100644 index 0000000000..431078fb18 --- /dev/null +++ b/engine/src/flutter/build/toolchain/mac/setup_toolchain.py @@ -0,0 +1,29 @@ +# 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. + +import os +import stat +import sys + +def CopyTool(source_path): + """Copies the given tool to the current directory, including a warning not + to edit it.""" + with open(source_path) as source_file: + tool_source = source_file.readlines() + + # Add header and write it out to the current directory (which should be the + # root build dir). + out_path = 'gyp-mac-tool' + with open(out_path, 'w') as tool_file: + tool_file.write(''.join([tool_source[0], + '# Generated by setup_toolchain.py do not edit.\n'] + + tool_source[1:])) + st = os.stat(out_path) + os.chmod(out_path, st.st_mode | stat.S_IEXEC) + +# Find the tool source, it's the first argument, and copy it. +if len(sys.argv) != 2: + print "Need one argument (mac_tool source path)." + sys.exit(1) +CopyTool(sys.argv[1]) diff --git a/engine/src/flutter/build/toolchain/nacl/BUILD.gn b/engine/src/flutter/build/toolchain/nacl/BUILD.gn new file mode 100644 index 0000000000..5fa637cb9f --- /dev/null +++ b/engine/src/flutter/build/toolchain/nacl/BUILD.gn @@ -0,0 +1,63 @@ +# 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. + +toolchain("x86_newlib") { + toolprefix = "gen/sdk/toolchain/linux_x86_newlib/bin/x86_64-nacl-" + cc = toolprefix + "gcc" + cxx = toolprefix + "g++" + ld = toolprefix + "g++" + + tool("cc") { + command = "$cc -MMD -MF \$out.d \$defines \$includes \$cflags \$cflags_c -c \$in -o \$out" + description = "CC(NaCl x86 Newlib) \$out" + depfile = "\$out.d" + depsformat = "gcc" + } + tool("cxx") { + # cflags_pch_cc + command = "$cxx -MMD -MF \$out.d \$defines \$includes \$cflags \$cflags_cc -c \$in -o \$out" + description = "CXX(NaCl x86 Newlib) \$out" + depfile = "\$out.d" + depsformat = "gcc" + } + tool("alink") { + command = "rm -f \$out && ${toolprefix}ar rcs \$out \$in" + description = "AR(NaCl x86 Newlib) \$out" + } + tool("solink") { + command = "if [ ! -e \$lib -o ! -e \${lib}.TOC ]; then $ld -shared \$ldflags -o \$lib -Wl,-soname=\$soname -Wl,--whole-archive \$in \$solibs -Wl,--no-whole-archive \$libs && { readelf -d \${lib} | grep SONAME ; nm -gD -f p \${lib} | cut -f1-2 -d' '; } > \${lib}.TOC; else $ld -shared \$ldflags -o \$lib -Wl,-soname=\$soname -Wl,--whole-archive \$in \$solibs -Wl,--no-whole-archive \$libs && { readelf -d \${lib} | grep SONAME ; nm -gD -f p \${lib} | cut -f1-2 -d' '; } > \${lib}.tmp && if ! cmp -s \${lib}.tmp \${lib}.TOC; then mv \${lib}.tmp \${lib}.TOC ; fi; fi" + description = "SOLINK(NaCl x86 Newlib) \$lib" + + #pool = "link_pool" + restat = "1" + } + tool("link") { + command = "$ld \$ldflags -o \$out -Wl,--start-group \$in \$solibs -Wl,--end-group \$libs" + description = "LINK(NaCl x86 Newlib) \$out" + + #pool = "link_pool" + } + + if (is_win) { + tool("stamp") { + command = "$python_path gyp-win-tool stamp \$out" + description = "STAMP \$out" + } + } else { + tool("stamp") { + command = "touch \$out" + description = "STAMP \$out" + } + } + + toolchain_args() { + # Override the default OS detection. The build config will set the is_* + # flags accordingly. + current_os = "nacl" + + # Component build not supported in NaCl, since it does not support shared + # libraries. + is_component_build = false + } +} diff --git a/engine/src/flutter/build/toolchain/win/BUILD.gn b/engine/src/flutter/build/toolchain/win/BUILD.gn new file mode 100644 index 0000000000..4936c3d0cf --- /dev/null +++ b/engine/src/flutter/build/toolchain/win/BUILD.gn @@ -0,0 +1,247 @@ +# 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. + +declare_args() { + # Path to the directory containing the VC binaries for the right + # combination of host and target architectures. Currently only the + # 64-bit host toolchain is supported, with either 32-bit or 64-bit targets. + # If vc_bin_dir is not specified on the command line (and it normally + # isn't), we will dynamically determine the right value to use at runtime. + vc_bin_dir = "" +} + +import("//build/config/win/visual_studio_version.gni") +import("//build/toolchain/goma.gni") + +# Should only be running on Windows. +assert(is_win) + +# Setup the Visual Studio state. +# +# Its arguments are the VS path and the compiler wrapper tool. It will write +# "environment.x86" and "environment.x64" to the build directory and return a +# list to us. +gyp_win_tool_path = + rebase_path("//tools/gyp/pylib/gyp/win_tool.py", root_build_dir) + +toolchain_data = exec_script("setup_toolchain.py", + [ + visual_studio_path, + gyp_win_tool_path, + windows_sdk_path, + visual_studio_runtime_dirs, + current_cpu, + ], + "scope") + +if (vc_bin_dir == "") { + vc_bin_dir = toolchain_data.vc_bin_dir +} + +if (use_goma) { + goma_prefix = "$goma_dir/gomacc.exe " +} else { + goma_prefix = "" +} + +# This value will be inherited in the toolchain below. +concurrent_links = exec_script("../get_concurrent_links.py", [], "value") + +# Parameters: +# current_cpu: current_cpu to pass as a build arg +# environment: File name of environment file. +template("msvc_toolchain") { + if (defined(invoker.concurrent_links)) { + concurrent_links = invoker.concurrent_links + } + + env = invoker.environment + + if (is_debug) { + configuration = "Debug" + } else { + configuration = "Release" + } + exec_script("../../vs_toolchain.py", + [ + "copy_dlls", + rebase_path(root_build_dir), + configuration, + invoker.current_cpu, + ]) + + cl = invoker.cl + + toolchain(target_name) { + # Make these apply to all tools below. + lib_switch = "" + lib_dir_switch = "/LIBPATH:" + + tool("cc") { + rspfile = "{{output}}.rsp" + pdbname = "{{target_out_dir}}/{{target_output_name}}_c.pdb" + command = "ninja -t msvc -e $env -- $cl /nologo /showIncludes /FC @$rspfile /c {{source}} /Fo{{output}} /Fd$pdbname" + depsformat = "msvc" + description = "CC {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj", + ] + rspfile_content = "{{defines}} {{include_dirs}} {{cflags}} {{cflags_c}}" + } + + tool("cxx") { + rspfile = "{{output}}.rsp" + + # The PDB name needs to be different between C and C++ compiled files. + pdbname = "{{target_out_dir}}/{{target_output_name}}_cc.pdb" + command = "ninja -t msvc -e $env -- $cl /nologo /showIncludes /FC @$rspfile /c {{source}} /Fo{{output}} /Fd$pdbname" + depsformat = "msvc" + description = "CXX {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj", + ] + rspfile_content = "{{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}}" + } + + tool("rc") { + command = "$python_path gyp-win-tool rc-wrapper $env rc.exe {{defines}} {{include_dirs}} /fo{{output}} {{source}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.res", + ] + description = "RC {{output}}" + } + + tool("asm") { + # TODO(brettw): "/safeseh" assembler argument is hardcoded here. Extract + # assembler flags to a variable like cflags. crbug.com/418613 + command = "$python_path gyp-win-tool asm-wrapper $env ml.exe {{defines}} {{include_dirs}} /safeseh /c /Fo {{output}} {{source}}" + description = "ASM {{output}}" + outputs = [ + "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.obj", + ] + } + + tool("alink") { + rspfile = "{{output}}.rsp" + command = "$python_path gyp-win-tool link-wrapper $env False lib.exe /nologo /ignore:4221 /OUT:{{output}} @$rspfile" + description = "LIB {{output}}" + outputs = [ + # Ignore {{output_extension}} and always use .lib, there's no reason to + # allow targets to override this extension on Windows. + "{{target_out_dir}}/{{target_output_name}}.lib", + ] + default_output_extension = ".lib" + + # The use of inputs_newline is to work around a fixed per-line buffer + # size in the linker. + rspfile_content = "{{inputs_newline}}" + } + + tool("solink") { + dllname = "{{root_out_dir}}/{{target_output_name}}{{output_extension}}" # e.g. foo.dll + libname = + "{{root_out_dir}}/{{target_output_name}}{{output_extension}}.lib" # e.g. foo.dll.lib + rspfile = "${dllname}.rsp" + + link_command = "$python_path gyp-win-tool link-wrapper $env False link.exe /nologo /IMPLIB:$libname /DLL /OUT:$dllname /PDB:${dllname}.pdb @$rspfile" + + # TODO(brettw) support manifests + #manifest_command = "$python_path gyp-win-tool manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:${dllname}.manifest" + #command = "cmd /c $link_command && $manifest_command" + command = link_command + + default_output_extension = ".dll" + description = "LINK(DLL) {{output}}" + outputs = [ + dllname, + libname, + ] + link_output = libname + depend_output = libname + + # The use of inputs_newline is to work around a fixed per-line buffer + # size in the linker. + rspfile_content = "{{libs}} {{solibs}} {{inputs_newline}} {{ldflags}}" + } + + tool("link") { + rspfile = "{{output}}.rsp" + + link_command = "$python_path gyp-win-tool link-wrapper $env False link.exe /nologo /OUT:{{output}} /PDB:{{output}}.pdb @$rspfile" + + # TODO(brettw) support manifests + #manifest_command = "$python_path gyp-win-tool manifest-wrapper $env mt.exe -nologo -manifest $manifests -out:{{output}}.manifest" + #command = "cmd /c $link_command && $manifest_command" + command = link_command + + default_output_extension = ".exe" + description = "LINK {{output}}" + outputs = [ + "{{root_out_dir}}/{{target_output_name}}{{output_extension}}", + ] + + # The use of inputs_newline is to work around a fixed per-line buffer + # size in the linker. + rspfile_content = "{{inputs_newline}} {{libs}} {{solibs}} {{ldflags}}" + } + + tool("stamp") { + command = "$python_path gyp-win-tool stamp {{output}}" + description = "STAMP {{output}}" + } + + tool("copy") { + command = + "$python_path gyp-win-tool recursive-mirror {{source}} {{output}}" + description = "COPY {{source}} {{output}}" + } + + # When invoking this toolchain not as the default one, these args will be + # passed to the build. They are ignored when this is the default toolchain. + toolchain_args() { + current_cpu = invoker.current_cpu + if (defined(invoker.is_clang)) { + is_clang = invoker.is_clang + } + } + } +} + +# TODO(dpranke): Declare both toolchains all of the time when we +# get it sorted out how we want to support them both in a single build. +# Right now only one of these can be enabled at a time because the +# runtime libraries get copied to root_build_dir and would collide. +if (current_cpu == "x86") { + msvc_toolchain("x86") { + environment = "environment.x86" + current_cpu = "x86" + cl = "${goma_prefix}\"${vc_bin_dir}/cl.exe\"" + is_clang = false + } + msvc_toolchain("clang_x86") { + environment = "environment.x86" + current_cpu = "x86" + prefix = rebase_path("//third_party/llvm-build/Release+Asserts/bin", + root_build_dir) + cl = "${goma_prefix}$prefix/clang-cl.exe" + is_clang = true + } +} + +if (current_cpu == "x64") { + msvc_toolchain("x64") { + environment = "environment.x64" + current_cpu = "x64" + cl = "${goma_prefix}\"${vc_bin_dir}/cl.exe\"" + is_clang = false + } + msvc_toolchain("clang_x64") { + environment = "environment.x64" + current_cpu = "x64" + prefix = rebase_path("//third_party/llvm-build/Release+Asserts/bin", + root_build_dir) + cl = "${goma_prefix}$prefix/clang-cl.exe" + is_clang = true + } +} diff --git a/engine/src/flutter/build/toolchain/win/midl.gni b/engine/src/flutter/build/toolchain/win/midl.gni new file mode 100644 index 0000000000..197f3668e3 --- /dev/null +++ b/engine/src/flutter/build/toolchain/win/midl.gni @@ -0,0 +1,105 @@ +# Copyright 2014 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. + +assert(is_win) + +import("//build/config/win/visual_studio_version.gni") + +# This template defines a rule to invoke the MS IDL compiler. +# +# Parameters +# +# sources +# List of .idl file to process. +# +# out_dir (optional) +# Directory to write the generated files to. Defaults to target_gen_dir. +# +# deps (optional) +# visibility (optional) + +template("midl") { + action_name = "${target_name}_idl_action" + source_set_name = target_name + + assert(defined(invoker.sources), "Source must be defined for $target_name") + + if (defined(invoker.out_dir)) { + out_dir = invoker.out_dir + } else { + out_dir = target_gen_dir + } + + header_file = "{{source_name_part}}.h" + dlldata_file = "{{source_name_part}}.dlldata.c" + interface_identifier_file = "{{source_name_part}}_i.c" + proxy_file = "{{source_name_part}}_p.c" + type_library_file = "{{source_name_part}}.tlb" + + action_foreach(action_name) { + visibility = [ ":$source_set_name" ] + + # This functionality is handled by the win-tool because the GYP build has + # MIDL support built-in. + # TODO(brettw) move this to a separate MIDL wrapper script for better + # clarity once GYP support is not needed. + script = "$root_build_dir/gyp-win-tool" + + sources = invoker.sources + + # Note that .tlb is not included in the outputs as it is not always + # generated depending on the content of the input idl file. + outputs = [ + "$out_dir/$header_file", + "$out_dir/$dlldata_file", + "$out_dir/$interface_identifier_file", + "$out_dir/$proxy_file", + ] + + if (current_cpu == "x86") { + win_tool_arch = "environment.x86" + idl_target_platform = "win32" + } else if (current_cpu == "x64") { + win_tool_arch = "environment.x64" + idl_target_platform = "x64" + } else { + assert(false, "Need environment for this arch") + } + + args = [ + "midl-wrapper", + win_tool_arch, + rebase_path(out_dir, root_build_dir), + type_library_file, + header_file, + dlldata_file, + interface_identifier_file, + proxy_file, + "{{source}}", + "/char", + "signed", + "/env", + idl_target_platform, + "/Oicf", + ] + + if (defined(invoker.deps)) { + deps = invoker.deps + } + } + + source_set(target_name) { + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + + # We only compile the IID files from the IDL tool rather than all outputs. + sources = process_file_template(invoker.sources, + [ "$out_dir/$interface_identifier_file" ]) + + public_deps = [ + ":$action_name", + ] + } +} diff --git a/engine/src/flutter/build/toolchain/win/setup_toolchain.py b/engine/src/flutter/build/toolchain/win/setup_toolchain.py new file mode 100644 index 0000000000..bc9bd1e7b4 --- /dev/null +++ b/engine/src/flutter/build/toolchain/win/setup_toolchain.py @@ -0,0 +1,154 @@ +# 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. +# +# Copies the given "win tool" (which the toolchain uses to wrap compiler +# invocations) and the environment blocks for the 32-bit and 64-bit builds on +# Windows to the build directory. +# +# The arguments are the visual studio install location and the location of the +# win tool. The script assumes that the root build directory is the current dir +# and the files will be written to the current directory. + +import errno +import os +import re +import subprocess +import sys + + +def _ExtractImportantEnvironment(output_of_set): + """Extracts environment variables required for the toolchain to run from + a textual dump output by the cmd.exe 'set' command.""" + envvars_to_save = ( + 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma. + 'include', + 'lib', + 'libpath', + 'path', + 'pathext', + 'systemroot', + 'temp', + 'tmp', + ) + env = {} + for line in output_of_set.splitlines(): + for envvar in envvars_to_save: + if re.match(envvar + '=', line.lower()): + var, setting = line.split('=', 1) + if envvar == 'path': + # Our own rules (for running gyp-win-tool) and other actions in + # Chromium rely on python being in the path. Add the path to this + # python here so that if it's not in the path when ninja is run + # later, python will still be found. + setting = os.path.dirname(sys.executable) + os.pathsep + setting + env[var.upper()] = setting + break + for required in ('SYSTEMROOT', 'TEMP', 'TMP'): + if required not in env: + raise Exception('Environment variable "%s" ' + 'required to be set to valid path' % required) + return env + + +def _SetupScript(target_cpu, sdk_dir): + """Returns a command (with arguments) to be used to set up the + environment.""" + # Check if we are running in the SDK command line environment and use + # the setup script from the SDK if so. |target_cpu| should be either + # 'x86' or 'x64'. + assert target_cpu in ('x86', 'x64') + if bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1))) and sdk_dir: + return [os.path.normpath(os.path.join(sdk_dir, 'Bin/SetEnv.Cmd')), + '/' + target_cpu] + else: + # We only support x64-hosted tools. + # TODO(scottmg|dpranke): Non-depot_tools toolchain: need to get Visual + # Studio install location from registry. + return [os.path.normpath(os.path.join(os.environ['GYP_MSVS_OVERRIDE_PATH'], + 'VC/vcvarsall.bat')), + 'amd64_x86' if target_cpu == 'x86' else 'amd64'] + + +def _FormatAsEnvironmentBlock(envvar_dict): + """Format as an 'environment block' directly suitable for CreateProcess. + Briefly this is a list of key=value\0, terminated by an additional \0. See + CreateProcess documentation for more details.""" + block = '' + nul = '\0' + for key, value in envvar_dict.iteritems(): + block += key + '=' + value + nul + block += nul + return block + + +def _CopyTool(source_path): + """Copies the given tool to the current directory, including a warning not + to edit it.""" + with open(source_path) as source_file: + tool_source = source_file.readlines() + + # Add header and write it out to the current directory (which should be the + # root build dir). + with open("gyp-win-tool", 'w') as tool_file: + tool_file.write(''.join([tool_source[0], + '# Generated by setup_toolchain.py do not edit.\n'] + + tool_source[1:])) + + +def main(): + if len(sys.argv) != 6: + print('Usage setup_toolchain.py ' + ' ' + ' ') + sys.exit(2) + tool_source = sys.argv[2] + win_sdk_path = sys.argv[3] + runtime_dirs = sys.argv[4] + target_cpu = sys.argv[5] + + _CopyTool(tool_source) + + cpus = ('x86', 'x64') + assert target_cpu in cpus + vc_bin_dir = '' + + # TODO(scottmg|goma): Do we need an equivalent of + # ninja_use_custom_environment_files? + + for cpu in cpus: + # Extract environment variables for subprocesses. + args = _SetupScript(cpu, win_sdk_path) + args.extend(('&&', 'set')) + popen = subprocess.Popen( + args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + variables, _ = popen.communicate() + env = _ExtractImportantEnvironment(variables) + env['PATH'] = runtime_dirs + ';' + env['PATH'] + + if cpu == target_cpu: + for path in env['PATH'].split(os.pathsep): + if os.path.exists(os.path.join(path, 'cl.exe')): + vc_bin_dir = os.path.realpath(path) + break + + # The Windows SDK include directories must be first. They both have a sal.h, + # and the SDK one is newer and the SDK uses some newer features from it not + # present in the Visual Studio one. + + if win_sdk_path: + additional_includes = ('{sdk_dir}\\Include\\shared;' + + '{sdk_dir}\\Include\\um;' + + '{sdk_dir}\\Include\\winrt;').format( + sdk_dir=win_sdk_path) + env['INCLUDE'] = additional_includes + env['INCLUDE'] + env_block = _FormatAsEnvironmentBlock(env) + with open('environment.' + cpu, 'wb') as f: + f.write(env_block) + + assert vc_bin_dir + print 'vc_bin_dir = "%s"' % vc_bin_dir + + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/build/tree_truth.sh b/engine/src/flutter/build/tree_truth.sh new file mode 100755 index 0000000000..617092dc8a --- /dev/null +++ b/engine/src/flutter/build/tree_truth.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# Copyright 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. +# +# Script for printing recent commits in a buildbot run. + +# Return the sha1 of the given tag. If not present, return "". +# $1: path to repo +# $2: tag name +tt_sha1_for_tag() { + oneline=$(cd $1 && git log -1 $2 --format='%H' 2>/dev/null) + if [ $? -eq 0 ] ; then + echo $oneline + fi +} + +# Return the sha1 of HEAD, or "" +# $1: path to repo +tt_sha1_for_head() { + ( cd $1 && git log HEAD -n1 --format='%H' | cat ) +} + +# For the given repo, set tag to HEAD. +# $1: path to repo +# $2: tag name +tt_tag_head() { + ( cd $1 && git tag -f $2 ) +} + +# For the given repo, delete the tag. +# $1: path to repo +# $2: tag name +tt_delete_tag() { + ( cd $1 && git tag -d $2 ) +} + +# For the given repo, set tag to "three commits ago" (for testing). +# $1: path to repo +# $2: tag name +tt_tag_three_ago() { + local sh=$(cd $1 && git log --pretty=oneline -n 3 | tail -1 | awk '{print $1}') + ( cd $1 && git tag -f $2 $sh ) +} + +# List the commits between the given tag and HEAD. +# If the tag does not exist, only list the last few. +# If the tag is at HEAD, list nothing. +# Output format has distinct build steps for repos with changes. +# $1: path to repo +# $2: tag name +# $3: simple/short repo name to use for display +tt_list_commits() { + local tag_sha1=$(tt_sha1_for_tag $1 $2) + local head_sha1=$(tt_sha1_for_head $1) + local display_name=$(echo $3 | sed 's#/#_#g') + if [ "${tag_sha1}" = "${head_sha1}" ] ; then + return + fi + if [ "${tag_sha1}" = "" ] ; then + echo "@@@BUILD_STEP Recent commits in repo $display_name@@@" + echo "NOTE: git tag was not found so we have no baseline." + echo "Here are some recent commits, but they may not be new for this build." + ( cd $1 && git log -n 10 --stat | cat) + else + echo "@@@BUILD_STEP New commits in repo $display_name@@@" + ( cd $1 && git log -n 500 $2..HEAD --stat | cat) + fi +} + +# Clean out the tree truth tags in all repos. For testing. +tt_clean_all() { + for project in $@; do + tt_delete_tag $CHROME_SRC/../$project tree_truth + done +} + +# Print tree truth for all clank repos. +tt_print_all() { + for project in $@; do + local full_path=$CHROME_SRC/../$project + tt_list_commits $full_path tree_truth $project + tt_tag_head $full_path tree_truth + done +} + +# Print a summary of the last 10 commits for each repo. +tt_brief_summary() { + echo "@@@BUILD_STEP Brief summary of recent CLs in every branch@@@" + for project in $@; do + echo $project: + local full_path=$CHROME_SRC/../$project + (cd $full_path && git log -n 10 --format=" %H %s %an, %ad" | cat) + echo "=================================================================" + done +} + +CHROME_SRC=$1 +shift +PROJECT_LIST=$@ +tt_brief_summary $PROJECT_LIST +tt_print_all $PROJECT_LIST diff --git a/engine/src/flutter/build/update-linux-sandbox.sh b/engine/src/flutter/build/update-linux-sandbox.sh new file mode 100755 index 0000000000..735733a0d0 --- /dev/null +++ b/engine/src/flutter/build/update-linux-sandbox.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +# Copyright (c) 2012 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. + +BUILDTYPE="${BUILDTYPE:-Debug}" +CHROME_SRC_DIR="${CHROME_SRC_DIR:-$(dirname -- $(readlink -fn -- "$0"))/..}" +CHROME_OUT_DIR="${CHROME_SRC_DIR}/${CHROMIUM_OUT_DIR:-out}/${BUILDTYPE}" +CHROME_SANDBOX_BUILD_PATH="${CHROME_OUT_DIR}/chrome_sandbox" +CHROME_SANDBOX_INST_PATH="/usr/local/sbin/chrome-devel-sandbox" +CHROME_SANDBOX_INST_DIR=$(dirname -- "$CHROME_SANDBOX_INST_PATH") + +TARGET_DIR_TYPE=$(stat -f -c %t -- "${CHROME_SANDBOX_INST_DIR}" 2>/dev/null) +if [ $? -ne 0 ]; then + echo "Could not get status of ${CHROME_SANDBOX_INST_DIR}" + exit 1 +fi + +# Make sure the path is not on NFS. +if [ "${TARGET_DIR_TYPE}" = "6969" ]; then + echo "Please make sure ${CHROME_SANDBOX_INST_PATH} is not on NFS!" + exit 1 +fi + +installsandbox() { + echo "(using sudo so you may be asked for your password)" + sudo -- cp "${CHROME_SANDBOX_BUILD_PATH}" \ + "${CHROME_SANDBOX_INST_PATH}" && + sudo -- chown root:root "${CHROME_SANDBOX_INST_PATH}" && + sudo -- chmod 4755 "${CHROME_SANDBOX_INST_PATH}" + return $? +} + +if [ ! -d "${CHROME_OUT_DIR}" ]; then + echo -n "${CHROME_OUT_DIR} does not exist. Use \"BUILDTYPE=Release ${0}\" " + echo "If you are building in Release mode" + exit 1 +fi + +if [ ! -f "${CHROME_SANDBOX_BUILD_PATH}" ]; then + echo -n "Could not find ${CHROME_SANDBOX_BUILD_PATH}, " + echo "please make sure you build the chrome_sandbox target" + exit 1 +fi + +if [ ! -f "${CHROME_SANDBOX_INST_PATH}" ]; then + echo -n "Could not find ${CHROME_SANDBOX_INST_PATH}, " + echo "installing it now." + installsandbox +fi + +if [ ! -f "${CHROME_SANDBOX_INST_PATH}" ]; then + echo "Failed to install ${CHROME_SANDBOX_INST_PATH}" + exit 1 +fi + +CURRENT_API=$("${CHROME_SANDBOX_BUILD_PATH}" --get-api) +INSTALLED_API=$("${CHROME_SANDBOX_INST_PATH}" --get-api) + +if [ "${CURRENT_API}" != "${INSTALLED_API}" ]; then + echo "Your installed setuid sandbox is too old, installing it now." + if ! installsandbox; then + echo "Failed to install ${CHROME_SANDBOX_INST_PATH}" + exit 1 + fi +else + echo "Your setuid sandbox is up to date" + if [ "${CHROME_DEVEL_SANDBOX}" != "${CHROME_SANDBOX_INST_PATH}" ]; then + echo -n "Make sure you have \"export " + echo -n "CHROME_DEVEL_SANDBOX=${CHROME_SANDBOX_INST_PATH}\" " + echo "somewhere in your .bashrc" + echo "This variable is currently: ${CHROME_DEVEL_SANDBOX:-empty}" + fi +fi diff --git a/engine/src/flutter/build/util/BUILD.gn b/engine/src/flutter/build/util/BUILD.gn new file mode 100644 index 0000000000..c18f0ce25b --- /dev/null +++ b/engine/src/flutter/build/util/BUILD.gn @@ -0,0 +1,28 @@ +# 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. + +action("webkit_version") { + script = "version.py" + + lastchange_file = "LASTCHANGE.blink" + + # TODO(brettw) move from content to this directory. + template_file = "//content/webkit_version.h.in" + inputs = [ + lastchange_file, + template_file, + ] + + output_file = "$root_gen_dir/webkit_version.h" + outputs = [ + output_file, + ] + + args = [ + "-f", + rebase_path(lastchange_file, root_build_dir), + rebase_path(template_file, root_build_dir), + rebase_path(output_file, root_build_dir), + ] +} diff --git a/engine/src/flutter/build/util/lastchange.py b/engine/src/flutter/build/util/lastchange.py new file mode 100755 index 0000000000..1a7f5199d2 --- /dev/null +++ b/engine/src/flutter/build/util/lastchange.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +""" +lastchange.py -- Chromium revision fetching utility. +""" + +import re +import optparse +import os +import subprocess +import sys + +_GIT_SVN_ID_REGEX = re.compile(r'.*git-svn-id:\s*([^@]*)@([0-9]+)', re.DOTALL) + +class VersionInfo(object): + def __init__(self, url, revision): + self.url = url + self.revision = revision + + +def FetchSVNRevision(directory, svn_url_regex): + """ + Fetch the Subversion branch and revision for a given directory. + + Errors are swallowed. + + Returns: + A VersionInfo object or None on error. + """ + try: + proc = subprocess.Popen(['svn', 'info'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=directory, + shell=(sys.platform=='win32')) + except OSError: + # command is apparently either not installed or not executable. + return None + if not proc: + return None + + attrs = {} + for line in proc.stdout: + line = line.strip() + if not line: + continue + key, val = line.split(': ', 1) + attrs[key] = val + + try: + match = svn_url_regex.search(attrs['URL']) + if match: + url = match.group(2) + else: + url = '' + revision = attrs['Revision'] + except KeyError: + return None + + return VersionInfo(url, revision) + + +def RunGitCommand(directory, command): + """ + Launches git subcommand. + + Errors are swallowed. + + Returns: + A process object or None. + """ + command = ['git'] + command + # Force shell usage under cygwin. This is a workaround for + # mysterious loss of cwd while invoking cygwin's git. + # We can't just pass shell=True to Popen, as under win32 this will + # cause CMD to be used, while we explicitly want a cygwin shell. + if sys.platform == 'cygwin': + command = ['sh', '-c', ' '.join(command)] + try: + proc = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=directory, + shell=(sys.platform=='win32')) + return proc + except OSError: + return None + + +def FetchGitRevision(directory): + """ + Fetch the Git hash for a given directory. + + Errors are swallowed. + + Returns: + A VersionInfo object or None on error. + """ + hsh = '' + proc = RunGitCommand(directory, ['rev-parse', 'HEAD']) + if proc: + output = proc.communicate()[0].strip() + if proc.returncode == 0 and output: + hsh = output + if not hsh: + return None + pos = '' + proc = RunGitCommand(directory, ['cat-file', 'commit', 'HEAD']) + if proc: + output = proc.communicate()[0] + if proc.returncode == 0 and output: + for line in reversed(output.splitlines()): + if line.startswith('Cr-Commit-Position:'): + pos = line.rsplit()[-1].strip() + break + if not pos: + return VersionInfo('git', hsh) + return VersionInfo('git', '%s-%s' % (hsh, pos)) + + +def FetchGitSVNURLAndRevision(directory, svn_url_regex): + """ + Fetch the Subversion URL and revision through Git. + + Errors are swallowed. + + Returns: + A tuple containing the Subversion URL and revision. + """ + proc = RunGitCommand(directory, ['log', '-1', '--format=%b']) + if proc: + output = proc.communicate()[0].strip() + if proc.returncode == 0 and output: + # Extract the latest SVN revision and the SVN URL. + # The target line is the last "git-svn-id: ..." line like this: + # git-svn-id: svn://svn.chromium.org/chrome/trunk/src@85528 0039d316.... + match = _GIT_SVN_ID_REGEX.search(output) + if match: + revision = match.group(2) + url_match = svn_url_regex.search(match.group(1)) + if url_match: + url = url_match.group(2) + else: + url = '' + return url, revision + return None, None + + +def FetchGitSVNRevision(directory, svn_url_regex): + """ + Fetch the Git-SVN identifier for the local tree. + + Errors are swallowed. + """ + url, revision = FetchGitSVNURLAndRevision(directory, svn_url_regex) + if url and revision: + return VersionInfo(url, revision) + return None + + +def FetchVersionInfo(default_lastchange, directory=None, + directory_regex_prior_to_src_url='chrome|blink|svn'): + """ + Returns the last change (in the form of a branch, revision tuple), + from some appropriate revision control system. + """ + svn_url_regex = re.compile( + r'.*/(' + directory_regex_prior_to_src_url + r')(/.*)') + + version_info = (FetchSVNRevision(directory, svn_url_regex) or + FetchGitSVNRevision(directory, svn_url_regex) or + FetchGitRevision(directory)) + if not version_info: + if default_lastchange and os.path.exists(default_lastchange): + revision = open(default_lastchange, 'r').read().strip() + version_info = VersionInfo(None, revision) + else: + version_info = VersionInfo(None, None) + return version_info + +def GetHeaderGuard(path): + """ + Returns the header #define guard for the given file path. + This treats everything after the last instance of "src/" as being a + relevant part of the guard. If there is no "src/", then the entire path + is used. + """ + src_index = path.rfind('src/') + if src_index != -1: + guard = path[src_index + 4:] + else: + guard = path + guard = guard.upper() + return guard.replace('/', '_').replace('.', '_').replace('\\', '_') + '_' + +def GetHeaderContents(path, define, version): + """ + Returns what the contents of the header file should be that indicate the given + revision. Note that the #define is specified as a string, even though it's + currently always a SVN revision number, in case we need to move to git hashes. + """ + header_guard = GetHeaderGuard(path) + + header_contents = """/* Generated by lastchange.py, do not edit.*/ + +#ifndef %(header_guard)s +#define %(header_guard)s + +#define %(define)s "%(version)s" + +#endif // %(header_guard)s +""" + header_contents = header_contents % { 'header_guard': header_guard, + 'define': define, + 'version': version } + return header_contents + +def WriteIfChanged(file_name, contents): + """ + Writes the specified contents to the specified file_name + iff the contents are different than the current contents. + """ + try: + old_contents = open(file_name, 'r').read() + except EnvironmentError: + pass + else: + if contents == old_contents: + return + os.unlink(file_name) + open(file_name, 'w').write(contents) + + +def main(argv=None): + if argv is None: + argv = sys.argv + + parser = optparse.OptionParser(usage="lastchange.py [options]") + parser.add_option("-d", "--default-lastchange", metavar="FILE", + help="Default last change input FILE.") + parser.add_option("-m", "--version-macro", + help="Name of C #define when using --header. Defaults to " + + "LAST_CHANGE.", + default="LAST_CHANGE") + parser.add_option("-o", "--output", metavar="FILE", + help="Write last change to FILE. " + + "Can be combined with --header to write both files.") + parser.add_option("", "--header", metavar="FILE", + help="Write last change to FILE as a C/C++ header. " + + "Can be combined with --output to write both files.") + parser.add_option("--revision-only", action='store_true', + help="Just print the SVN revision number. Overrides any " + + "file-output-related options.") + parser.add_option("-s", "--source-dir", metavar="DIR", + help="Use repository in the given directory.") + opts, args = parser.parse_args(argv[1:]) + + out_file = opts.output + header = opts.header + + while len(args) and out_file is None: + if out_file is None: + out_file = args.pop(0) + if args: + sys.stderr.write('Unexpected arguments: %r\n\n' % args) + parser.print_help() + sys.exit(2) + + if opts.source_dir: + src_dir = opts.source_dir + else: + src_dir = os.path.dirname(os.path.abspath(__file__)) + + version_info = FetchVersionInfo(opts.default_lastchange, src_dir) + + if version_info.revision == None: + version_info.revision = '0' + + if opts.revision_only: + print version_info.revision + else: + contents = "LASTCHANGE=%s\n" % version_info.revision + if not out_file and not opts.header: + sys.stdout.write(contents) + else: + if out_file: + WriteIfChanged(out_file, contents) + if header: + WriteIfChanged(header, + GetHeaderContents(header, opts.version_macro, + version_info.revision)) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/util/lib/common/__init__.py b/engine/src/flutter/build/util/lib/common/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/engine/src/flutter/build/util/lib/common/perf_result_data_type.py b/engine/src/flutter/build/util/lib/common/perf_result_data_type.py new file mode 100644 index 0000000000..67b550a46c --- /dev/null +++ b/engine/src/flutter/build/util/lib/common/perf_result_data_type.py @@ -0,0 +1,20 @@ +# Copyright 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. + +DEFAULT = 'default' +UNIMPORTANT = 'unimportant' +HISTOGRAM = 'histogram' +UNIMPORTANT_HISTOGRAM = 'unimportant-histogram' +INFORMATIONAL = 'informational' + +ALL_TYPES = [DEFAULT, UNIMPORTANT, HISTOGRAM, UNIMPORTANT_HISTOGRAM, + INFORMATIONAL] + + +def IsValidType(datatype): + return datatype in ALL_TYPES + + +def IsHistogram(datatype): + return (datatype == HISTOGRAM or datatype == UNIMPORTANT_HISTOGRAM) diff --git a/engine/src/flutter/build/util/lib/common/perf_tests_results_helper.py b/engine/src/flutter/build/util/lib/common/perf_tests_results_helper.py new file mode 100644 index 0000000000..6cb058b2df --- /dev/null +++ b/engine/src/flutter/build/util/lib/common/perf_tests_results_helper.py @@ -0,0 +1,166 @@ +# Copyright 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. + +import re +import sys + +import json +import logging +import math + +import perf_result_data_type + + +# Mapping from result type to test output +RESULT_TYPES = {perf_result_data_type.UNIMPORTANT: 'RESULT ', + perf_result_data_type.DEFAULT: '*RESULT ', + perf_result_data_type.INFORMATIONAL: '', + perf_result_data_type.UNIMPORTANT_HISTOGRAM: 'HISTOGRAM ', + perf_result_data_type.HISTOGRAM: '*HISTOGRAM '} + + +def _EscapePerfResult(s): + """Escapes |s| for use in a perf result.""" + return re.sub('[\:|=/#&,]', '_', s) + + +def FlattenList(values): + """Returns a simple list without sub-lists.""" + ret = [] + for entry in values: + if isinstance(entry, list): + ret.extend(FlattenList(entry)) + else: + ret.append(entry) + return ret + + +def GeomMeanAndStdDevFromHistogram(histogram_json): + histogram = json.loads(histogram_json) + # Handle empty histograms gracefully. + if not 'buckets' in histogram: + return 0.0, 0.0 + count = 0 + sum_of_logs = 0 + for bucket in histogram['buckets']: + if 'high' in bucket: + bucket['mean'] = (bucket['low'] + bucket['high']) / 2.0 + else: + bucket['mean'] = bucket['low'] + if bucket['mean'] > 0: + sum_of_logs += math.log(bucket['mean']) * bucket['count'] + count += bucket['count'] + + if count == 0: + return 0.0, 0.0 + + sum_of_squares = 0 + geom_mean = math.exp(sum_of_logs / count) + for bucket in histogram['buckets']: + if bucket['mean'] > 0: + sum_of_squares += (bucket['mean'] - geom_mean) ** 2 * bucket['count'] + return geom_mean, math.sqrt(sum_of_squares / count) + + +def _ValueToString(v): + # Special case for floats so we don't print using scientific notation. + if isinstance(v, float): + return '%f' % v + else: + return str(v) + + +def _MeanAndStdDevFromList(values): + avg = None + sd = None + if len(values) > 1: + try: + value = '[%s]' % ','.join([_ValueToString(v) for v in values]) + avg = sum([float(v) for v in values]) / len(values) + sqdiffs = [(float(v) - avg) ** 2 for v in values] + variance = sum(sqdiffs) / (len(values) - 1) + sd = math.sqrt(variance) + except ValueError: + value = ', '.join(values) + else: + value = values[0] + return value, avg, sd + + +def PrintPages(page_list): + """Prints list of pages to stdout in the format required by perf tests.""" + print 'Pages: [%s]' % ','.join([_EscapePerfResult(p) for p in page_list]) + + +def PrintPerfResult(measurement, trace, values, units, + result_type=perf_result_data_type.DEFAULT, + print_to_stdout=True): + """Prints numerical data to stdout in the format required by perf tests. + + The string args may be empty but they must not contain any colons (:) or + equals signs (=). + This is parsed by the buildbot using: + http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/slave/process_log_utils.py + + Args: + measurement: A description of the quantity being measured, e.g. "vm_peak". + On the dashboard, this maps to a particular graph. Mandatory. + trace: A description of the particular data point, e.g. "reference". + On the dashboard, this maps to a particular "line" in the graph. + Mandatory. + values: A list of numeric measured values. An N-dimensional list will be + flattened and treated as a simple list. + units: A description of the units of measure, e.g. "bytes". + result_type: Accepts values of perf_result_data_type.ALL_TYPES. + print_to_stdout: If True, prints the output in stdout instead of returning + the output to caller. + + Returns: + String of the formated perf result. + """ + assert perf_result_data_type.IsValidType(result_type), \ + 'result type: %s is invalid' % result_type + + trace_name = _EscapePerfResult(trace) + + if (result_type == perf_result_data_type.UNIMPORTANT or + result_type == perf_result_data_type.DEFAULT or + result_type == perf_result_data_type.INFORMATIONAL): + assert isinstance(values, list) + assert '/' not in measurement + flattened_values = FlattenList(values) + assert len(flattened_values) + value, avg, sd = _MeanAndStdDevFromList(flattened_values) + output = '%s%s: %s%s%s %s' % ( + RESULT_TYPES[result_type], + _EscapePerfResult(measurement), + trace_name, + # Do not show equal sign if the trace is empty. Usually it happens when + # measurement is enough clear to describe the result. + '= ' if trace_name else '', + value, + units) + else: + assert perf_result_data_type.IsHistogram(result_type) + assert isinstance(values, list) + # The histograms can only be printed individually, there's no computation + # across different histograms. + assert len(values) == 1 + value = values[0] + output = '%s%s: %s= %s %s' % ( + RESULT_TYPES[result_type], + _EscapePerfResult(measurement), + trace_name, + value, + units) + avg, sd = GeomMeanAndStdDevFromHistogram(value) + + if avg: + output += '\nAvg %s: %f%s' % (measurement, avg, units) + if sd: + output += '\nSd %s: %f%s' % (measurement, sd, units) + if print_to_stdout: + print output + sys.stdout.flush() + return output diff --git a/engine/src/flutter/build/util/lib/common/unittest_util.py b/engine/src/flutter/build/util/lib/common/unittest_util.py new file mode 100644 index 0000000000..e586224aac --- /dev/null +++ b/engine/src/flutter/build/util/lib/common/unittest_util.py @@ -0,0 +1,151 @@ +# Copyright 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. + +"""Utilities for dealing with the python unittest module.""" + +import fnmatch +import sys +import unittest + + +class _TextTestResult(unittest._TextTestResult): + """A test result class that can print formatted text results to a stream. + + Results printed in conformance with gtest output format, like: + [ RUN ] autofill.AutofillTest.testAutofillInvalid: "test desc." + [ OK ] autofill.AutofillTest.testAutofillInvalid + [ RUN ] autofill.AutofillTest.testFillProfile: "test desc." + [ OK ] autofill.AutofillTest.testFillProfile + [ RUN ] autofill.AutofillTest.testFillProfileCrazyCharacters: "Test." + [ OK ] autofill.AutofillTest.testFillProfileCrazyCharacters + """ + def __init__(self, stream, descriptions, verbosity): + unittest._TextTestResult.__init__(self, stream, descriptions, verbosity) + self._fails = set() + + def _GetTestURI(self, test): + return '%s.%s.%s' % (test.__class__.__module__, + test.__class__.__name__, + test._testMethodName) + + def getDescription(self, test): + return '%s: "%s"' % (self._GetTestURI(test), test.shortDescription()) + + def startTest(self, test): + unittest.TestResult.startTest(self, test) + self.stream.writeln('[ RUN ] %s' % self.getDescription(test)) + + def addSuccess(self, test): + unittest.TestResult.addSuccess(self, test) + self.stream.writeln('[ OK ] %s' % self._GetTestURI(test)) + + def addError(self, test, err): + unittest.TestResult.addError(self, test, err) + self.stream.writeln('[ ERROR ] %s' % self._GetTestURI(test)) + self._fails.add(self._GetTestURI(test)) + + def addFailure(self, test, err): + unittest.TestResult.addFailure(self, test, err) + self.stream.writeln('[ FAILED ] %s' % self._GetTestURI(test)) + self._fails.add(self._GetTestURI(test)) + + def getRetestFilter(self): + return ':'.join(self._fails) + + +class TextTestRunner(unittest.TextTestRunner): + """Test Runner for displaying test results in textual format. + + Results are displayed in conformance with google test output. + """ + + def __init__(self, verbosity=1): + unittest.TextTestRunner.__init__(self, stream=sys.stderr, + verbosity=verbosity) + + def _makeResult(self): + return _TextTestResult(self.stream, self.descriptions, self.verbosity) + + +def GetTestsFromSuite(suite): + """Returns all the tests from a given test suite.""" + tests = [] + for x in suite: + if isinstance(x, unittest.TestSuite): + tests += GetTestsFromSuite(x) + else: + tests += [x] + return tests + + +def GetTestNamesFromSuite(suite): + """Returns a list of every test name in the given suite.""" + return map(lambda x: GetTestName(x), GetTestsFromSuite(suite)) + + +def GetTestName(test): + """Gets the test name of the given unittest test.""" + return '.'.join([test.__class__.__module__, + test.__class__.__name__, + test._testMethodName]) + + +def FilterTestSuite(suite, gtest_filter): + """Returns a new filtered tests suite based on the given gtest filter. + + See http://code.google.com/p/googletest/wiki/AdvancedGuide + for gtest_filter specification. + """ + return unittest.TestSuite(FilterTests(GetTestsFromSuite(suite), gtest_filter)) + + +def FilterTests(all_tests, gtest_filter): + """Filter a list of tests based on the given gtest filter. + + Args: + all_tests: List of tests (unittest.TestSuite) + gtest_filter: Filter to apply. + + Returns: + Filtered subset of the given list of tests. + """ + test_names = [GetTestName(test) for test in all_tests] + filtered_names = FilterTestNames(test_names, gtest_filter) + return [test for test in all_tests if GetTestName(test) in filtered_names] + + +def FilterTestNames(all_tests, gtest_filter): + """Filter a list of test names based on the given gtest filter. + + See http://code.google.com/p/googletest/wiki/AdvancedGuide + for gtest_filter specification. + + Args: + all_tests: List of test names. + gtest_filter: Filter to apply. + + Returns: + Filtered subset of the given list of test names. + """ + pattern_groups = gtest_filter.split('-') + positive_patterns = pattern_groups[0].split(':') + negative_patterns = None + if len(pattern_groups) > 1: + negative_patterns = pattern_groups[1].split(':') + + tests = [] + for test in all_tests: + # Test name must by matched by one positive pattern. + for pattern in positive_patterns: + if fnmatch.fnmatch(test, pattern): + break + else: + continue + # Test name must not be matched by any negative patterns. + for pattern in negative_patterns or []: + if fnmatch.fnmatch(test, pattern): + break + else: + tests += [test] + return tests diff --git a/engine/src/flutter/build/util/lib/common/util.py b/engine/src/flutter/build/util/lib/common/util.py new file mode 100644 index 0000000000..a415b1f534 --- /dev/null +++ b/engine/src/flutter/build/util/lib/common/util.py @@ -0,0 +1,151 @@ +# Copyright 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. + +"""Generic utilities for all python scripts.""" + +import atexit +import httplib +import os +import signal +import stat +import subprocess +import sys +import tempfile +import urlparse + + +def GetPlatformName(): + """Return a string to be used in paths for the platform.""" + if IsWindows(): + return 'win' + if IsMac(): + return 'mac' + if IsLinux(): + return 'linux' + raise NotImplementedError('Unknown platform "%s".' % sys.platform) + + +def IsWindows(): + return sys.platform == 'cygwin' or sys.platform.startswith('win') + + +def IsLinux(): + return sys.platform.startswith('linux') + + +def IsMac(): + return sys.platform.startswith('darwin') + + +def _DeleteDir(path): + """Deletes a directory recursively, which must exist.""" + # Don't use shutil.rmtree because it can't delete read-only files on Win. + for root, dirs, files in os.walk(path, topdown=False): + for name in files: + filename = os.path.join(root, name) + os.chmod(filename, stat.S_IWRITE) + os.remove(filename) + for name in dirs: + os.rmdir(os.path.join(root, name)) + os.rmdir(path) + + +def Delete(path): + """Deletes the given file or directory (recursively), which must exist.""" + if os.path.isdir(path): + _DeleteDir(path) + else: + os.remove(path) + + +def MaybeDelete(path): + """Deletes the given file or directory (recurisvely), if it exists.""" + if os.path.exists(path): + Delete(path) + + +def MakeTempDir(parent_dir=None): + """Creates a temporary directory and returns an absolute path to it. + + The temporary directory is automatically deleted when the python interpreter + exits normally. + + Args: + parent_dir: the directory to create the temp dir in. If None, the system + temp dir is used. + + Returns: + The absolute path to the temporary directory. + """ + path = tempfile.mkdtemp(dir=parent_dir) + atexit.register(MaybeDelete, path) + return path + + +def Unzip(zip_path, output_dir): + """Unzips the given zip file using a system installed unzip tool. + + Args: + zip_path: zip file to unzip. + output_dir: directory to unzip the contents of the zip file. The directory + must exist. + + Raises: + RuntimeError if the unzip operation fails. + """ + if IsWindows(): + unzip_cmd = ['C:\\Program Files\\7-Zip\\7z.exe', 'x', '-y'] + else: + unzip_cmd = ['unzip', '-o'] + unzip_cmd += [zip_path] + if RunCommand(unzip_cmd, output_dir) != 0: + raise RuntimeError('Unable to unzip %s to %s' % (zip_path, output_dir)) + + +def Kill(pid): + """Terminate the given pid.""" + if IsWindows(): + subprocess.call(['taskkill.exe', '/T', '/F', '/PID', str(pid)]) + else: + os.kill(pid, signal.SIGTERM) + + +def RunCommand(cmd, cwd=None): + """Runs the given command and returns the exit code. + + Args: + cmd: list of command arguments. + cwd: working directory to execute the command, or None if the current + working directory should be used. + + Returns: + The exit code of the command. + """ + process = subprocess.Popen(cmd, cwd=cwd) + process.wait() + return process.returncode + + +def DoesUrlExist(url): + """Determines whether a resource exists at the given URL. + + Args: + url: URL to be verified. + + Returns: + True if url exists, otherwise False. + """ + parsed = urlparse.urlparse(url) + try: + conn = httplib.HTTPConnection(parsed.netloc) + conn.request('HEAD', parsed.path) + response = conn.getresponse() + except (socket.gaierror, socket.error): + return False + finally: + conn.close() + # Follow both permanent (301) and temporary (302) redirects. + if response.status == 302 or response.status == 301: + return DoesUrlExist(response.getheader('location')) + return response.status == 200 diff --git a/engine/src/flutter/build/util/version.py b/engine/src/flutter/build/util/version.py new file mode 100755 index 0000000000..4d3691ae37 --- /dev/null +++ b/engine/src/flutter/build/util/version.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +""" +version.py -- Chromium version string substitution utility. +""" + +import argparse +import os +import sys + + +def fetch_values_from_file(values_dict, file_name): + """ + Fetches KEYWORD=VALUE settings from the specified file. + + Everything to the left of the first '=' is the keyword, + everything to the right is the value. No stripping of + white space, so beware. + + The file must exist, otherwise you get the Python exception from open(). + """ + for line in open(file_name, 'r').readlines(): + key, val = line.rstrip('\r\n').split('=', 1) + values_dict[key] = val + + +def fetch_values(file_list): + """ + Returns a dictionary of values to be used for substitution, populating + the dictionary with KEYWORD=VALUE settings from the files in 'file_list'. + + Explicitly adds the following value from internal calculations: + + OFFICIAL_BUILD + """ + CHROME_BUILD_TYPE = os.environ.get('CHROME_BUILD_TYPE') + if CHROME_BUILD_TYPE == '_official': + official_build = '1' + else: + official_build = '0' + + values = dict( + OFFICIAL_BUILD = official_build, + ) + + for file_name in file_list: + fetch_values_from_file(values, file_name) + + return values + + +def subst_template(contents, values): + """ + Returns the template with substituted values from the specified dictionary. + + Keywords to be substituted are surrounded by '@': @KEYWORD@. + + No attempt is made to avoid recursive substitution. The order + of evaluation is random based on the order of the keywords returned + by the Python dictionary. So do NOT substitute a value that + contains any @KEYWORD@ strings expecting them to be recursively + substituted, okay? + """ + for key, val in values.iteritems(): + try: + contents = contents.replace('@' + key + '@', val) + except TypeError: + print repr(key), repr(val) + return contents + + +def subst_file(file_name, values): + """ + Returns the contents of the specified file_name with substituted + values from the specified dictionary. + + This is like subst_template, except it operates on a file. + """ + template = open(file_name, 'r').read() + return subst_template(template, values); + + +def write_if_changed(file_name, contents): + """ + Writes the specified contents to the specified file_name + iff the contents are different than the current contents. + """ + try: + old_contents = open(file_name, 'r').read() + except EnvironmentError: + pass + else: + if contents == old_contents: + return + os.unlink(file_name) + open(file_name, 'w').write(contents) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-f', '--file', action='append', default=[], + help='Read variables from FILE.') + parser.add_argument('-i', '--input', default=None, + help='Read strings to substitute from FILE.') + parser.add_argument('-o', '--output', default=None, + help='Write substituted strings to FILE.') + parser.add_argument('-t', '--template', default=None, + help='Use TEMPLATE as the strings to substitute.') + parser.add_argument('-e', '--eval', action='append', default=[], + help='Evaluate VAL after reading variables. Can be used ' + 'to synthesize variables. e.g. -e \'PATCH_HI=int(' + 'PATCH)/256.') + parser.add_argument('args', nargs=argparse.REMAINDER, + help='For compatibility: INPUT and OUTPUT can be ' + 'passed as positional arguments.') + options = parser.parse_args() + + evals = {} + for expression in options.eval: + try: + evals.update(dict([expression.split('=', 1)])) + except ValueError: + parser.error('-e requires VAR=VAL') + + # Compatibility with old versions that considered the first two positional + # arguments shorthands for --input and --output. + while len(options.args) and (options.input is None or \ + options.output is None): + if options.input is None: + options.input = options.args.pop(0) + elif options.output is None: + options.output = options.args.pop(0) + if options.args: + parser.error('Unexpected arguments: %r' % options.args) + + values = fetch_values(options.file) + for key, val in evals.iteritems(): + values[key] = str(eval(val, globals(), values)) + + if options.template is not None: + contents = subst_template(options.template, values) + elif options.input: + contents = subst_file(options.input, values) + else: + # Generate a default set of version information. + contents = """MAJOR=%(MAJOR)s +MINOR=%(MINOR)s +BUILD=%(BUILD)s +PATCH=%(PATCH)s +LASTCHANGE=%(LASTCHANGE)s +OFFICIAL_BUILD=%(OFFICIAL_BUILD)s +""" % values + + if options.output is not None: + write_if_changed(options.output, contents) + else: + print contents + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/vs_toolchain.py b/engine/src/flutter/build/vs_toolchain.py new file mode 100644 index 0000000000..16f4477556 --- /dev/null +++ b/engine/src/flutter/build/vs_toolchain.py @@ -0,0 +1,259 @@ +# Copyright 2014 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. + +import json +import os +import pipes +import shutil +import subprocess +import sys + + +script_dir = os.path.dirname(os.path.realpath(__file__)) +chrome_src = os.path.abspath(os.path.join(script_dir, os.pardir)) +SRC_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(1, os.path.join(chrome_src, 'tools')) +sys.path.insert(0, os.path.join(chrome_src, 'tools', 'gyp', 'pylib')) +json_data_file = os.path.join(script_dir, 'win_toolchain.json') + + +import gyp + + +def SetEnvironmentAndGetRuntimeDllDirs(): + """Sets up os.environ to use the depot_tools VS toolchain with gyp, and + returns the location of the VS runtime DLLs so they can be copied into + the output directory after gyp generation. + """ + vs2013_runtime_dll_dirs = None + depot_tools_win_toolchain = \ + bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))) + if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain: + if not os.path.exists(json_data_file): + Update() + with open(json_data_file, 'r') as tempf: + toolchain_data = json.load(tempf) + + toolchain = toolchain_data['path'] + version = toolchain_data['version'] + win_sdk = toolchain_data.get('win_sdk') + if not win_sdk: + win_sdk = toolchain_data['win8sdk'] + wdk = toolchain_data['wdk'] + # TODO(scottmg): The order unfortunately matters in these. They should be + # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call + # below). http://crbug.com/345992 + vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs'] + + os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain + os.environ['GYP_MSVS_VERSION'] = version + # We need to make sure windows_sdk_path is set to the automated + # toolchain values in GYP_DEFINES, but don't want to override any + # otheroptions.express + # values there. + gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES')) + gyp_defines_dict['windows_sdk_path'] = win_sdk + os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v))) + for k, v in gyp_defines_dict.iteritems()) + os.environ['WINDOWSSDKDIR'] = win_sdk + os.environ['WDK_DIR'] = wdk + # Include the VS runtime in the PATH in case it's not machine-installed. + runtime_path = ';'.join(vs2013_runtime_dll_dirs) + os.environ['PATH'] = runtime_path + ';' + os.environ['PATH'] + return vs2013_runtime_dll_dirs + + +def _VersionNumber(): + """Gets the standard version number ('120', '140', etc.) based on + GYP_MSVS_VERSION.""" + if os.environ['GYP_MSVS_VERSION'] == '2013': + return '120' + elif os.environ['GYP_MSVS_VERSION'] == '2015': + return '140' + else: + raise ValueError('Unexpected GYP_MSVS_VERSION') + + +def _CopyRuntimeImpl(target, source): + """Copy |source| to |target| if it doesn't already exist or if it + needs to be updated. + """ + if (os.path.isdir(os.path.dirname(target)) and + (not os.path.isfile(target) or + os.stat(target).st_mtime != os.stat(source).st_mtime)): + print 'Copying %s to %s...' % (source, target) + if os.path.exists(target): + os.unlink(target) + shutil.copy2(source, target) + + +def _CopyRuntime2013(target_dir, source_dir, dll_pattern): + """Copy both the msvcr and msvcp runtime DLLs, only if the target doesn't + exist, but the target directory does exist.""" + for file_part in ('p', 'r'): + dll = dll_pattern % file_part + target = os.path.join(target_dir, dll) + source = os.path.join(source_dir, dll) + _CopyRuntimeImpl(target, source) + + +def _CopyRuntime2015(target_dir, source_dir, dll_pattern): + """Copy both the msvcp and vccorlib runtime DLLs, only if the target doesn't + exist, but the target directory does exist.""" + for file_part in ('msvcp', 'vccorlib'): + dll = dll_pattern % file_part + target = os.path.join(target_dir, dll) + source = os.path.join(source_dir, dll) + _CopyRuntimeImpl(target, source) + + +def CopyVsRuntimeDlls(output_dir, runtime_dirs): + """Copies the VS runtime DLLs from the given |runtime_dirs| to the output + directory so that even if not system-installed, built binaries are likely to + be able to run. + + This needs to be run after gyp has been run so that the expected target + output directories are already created. + """ + assert sys.platform.startswith(('win32', 'cygwin')) + + x86, x64 = runtime_dirs + out_debug = os.path.join(output_dir, 'Debug') + out_debug_nacl64 = os.path.join(output_dir, 'Debug', 'x64') + out_release = os.path.join(output_dir, 'Release') + out_release_nacl64 = os.path.join(output_dir, 'Release', 'x64') + out_debug_x64 = os.path.join(output_dir, 'Debug_x64') + out_release_x64 = os.path.join(output_dir, 'Release_x64') + + if os.path.exists(out_debug) and not os.path.exists(out_debug_nacl64): + os.makedirs(out_debug_nacl64) + if os.path.exists(out_release) and not os.path.exists(out_release_nacl64): + os.makedirs(out_release_nacl64) + if os.environ.get('GYP_MSVS_VERSION') == '2015': + _CopyRuntime2015(out_debug, x86, '%s140d.dll') + _CopyRuntime2015(out_release, x86, '%s140.dll') + _CopyRuntime2015(out_debug_x64, x64, '%s140d.dll') + _CopyRuntime2015(out_release_x64, x64, '%s140.dll') + _CopyRuntime2015(out_debug_nacl64, x64, '%s140d.dll') + _CopyRuntime2015(out_release_nacl64, x64, '%s140.dll') + else: + # VS2013 is the default. + _CopyRuntime2013(out_debug, x86, 'msvc%s120d.dll') + _CopyRuntime2013(out_release, x86, 'msvc%s120.dll') + _CopyRuntime2013(out_debug_x64, x64, 'msvc%s120d.dll') + _CopyRuntime2013(out_release_x64, x64, 'msvc%s120.dll') + _CopyRuntime2013(out_debug_nacl64, x64, 'msvc%s120d.dll') + _CopyRuntime2013(out_release_nacl64, x64, 'msvc%s120.dll') + + # Copy the PGO runtime library to the release directories. + if os.environ.get('GYP_MSVS_OVERRIDE_PATH'): + pgo_x86_runtime_dir = os.path.join(os.environ.get('GYP_MSVS_OVERRIDE_PATH'), + 'VC', 'bin') + pgo_x64_runtime_dir = os.path.join(pgo_x86_runtime_dir, 'amd64') + pgo_runtime_dll = 'pgort' + _VersionNumber() + '.dll' + source_x86 = os.path.join(pgo_x86_runtime_dir, pgo_runtime_dll) + if os.path.exists(source_x86): + _CopyRuntimeImpl(os.path.join(out_release, pgo_runtime_dll), source_x86) + source_x64 = os.path.join(pgo_x64_runtime_dir, pgo_runtime_dll) + if os.path.exists(source_x64): + _CopyRuntimeImpl(os.path.join(out_release_x64, pgo_runtime_dll), + source_x64) + + +def CopyDlls(target_dir, configuration, target_cpu): + """Copy the VS runtime DLLs into the requested directory as needed. + + configuration is one of 'Debug' or 'Release'. + target_cpu is one of 'x86' or 'x64'. + + The debug configuration gets both the debug and release DLLs; the + release config only the latter. + """ + vs2013_runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs() + if not vs2013_runtime_dll_dirs: + return + + x64_runtime, x86_runtime = vs2013_runtime_dll_dirs + runtime_dir = x64_runtime if target_cpu == 'x64' else x86_runtime + _CopyRuntime2013( + target_dir, runtime_dir, 'msvc%s' + _VersionNumber() + '.dll') + if configuration == 'Debug': + _CopyRuntime2013( + target_dir, runtime_dir, 'msvc%s' + _VersionNumber() + 'd.dll') + + +def _GetDesiredVsToolchainHashes(): + """Load a list of SHA1s corresponding to the toolchains that we want installed + to build with.""" + # TODO(scottmg): If explicitly set to VS2015 override hashes to the VS2015 RC + # toolchain. http://crbug.com/492774. + if os.environ.get('GYP_MSVS_VERSION') == '2015': + return ['40721575c85171cea5d7afe5ec17bd108a94796e'] + else: + # Default to VS2013. + return ['ee7d718ec60c2dc5d255bbe325909c2021a7efef'] + + +def Update(): + """Requests an update of the toolchain to the specific hashes we have at + this revision. The update outputs a .json of the various configuration + information required to pass to gyp which we use in |GetToolchainDir()|. + """ + depot_tools_win_toolchain = \ + bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))) + if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain: + import find_depot_tools + depot_tools_path = find_depot_tools.add_depot_tools_to_path() + get_toolchain_args = [ + sys.executable, + os.path.join(depot_tools_path, + 'win_toolchain', + 'get_toolchain_if_necessary.py'), + '--output-json', json_data_file, + ] + _GetDesiredVsToolchainHashes() + subprocess.check_call(get_toolchain_args) + + return 0 + + +def GetToolchainDir(): + """Gets location information about the current toolchain (must have been + previously updated by 'update'). This is used for the GN build.""" + runtime_dll_dirs = SetEnvironmentAndGetRuntimeDllDirs() + + # If WINDOWSSDKDIR is not set, search the default SDK path and set it. + if not 'WINDOWSSDKDIR' in os.environ: + default_sdk_path = 'C:\\Program Files (x86)\\Windows Kits\\8.1' + if os.path.isdir(default_sdk_path): + os.environ['WINDOWSSDKDIR'] = default_sdk_path + + print '''vs_path = "%s" +sdk_path = "%s" +vs_version = "%s" +wdk_dir = "%s" +runtime_dirs = "%s" +''' % ( + os.environ['GYP_MSVS_OVERRIDE_PATH'], + os.environ['WINDOWSSDKDIR'], + os.environ['GYP_MSVS_VERSION'], + os.environ.get('WDK_DIR', ''), + ';'.join(runtime_dll_dirs or ['None'])) + + +def main(): + if not sys.platform.startswith(('win32', 'cygwin')): + return 0 + commands = { + 'update': Update, + 'get_toolchain_dir': GetToolchainDir, + 'copy_dlls': CopyDlls, + } + if len(sys.argv) < 2 or sys.argv[1] not in commands: + print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands) + return 1 + return commands[sys.argv[1]](*sys.argv[2:]) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/build/whitespace_file.txt b/engine/src/flutter/build/whitespace_file.txt new file mode 100644 index 0000000000..ea82f4e4ca --- /dev/null +++ b/engine/src/flutter/build/whitespace_file.txt @@ -0,0 +1,156 @@ +Copyright 2014 The Chromium Authors. All rights reserved. +Use of this useless file is governed by a BSD-style license that can be +found in the LICENSE file. + + +This file is used for making non-code changes to trigger buildbot cycles. Make +any modification below this line. + +====================================================================== + +Let's make a story. Add zero+ sentences for every commit: + +CHÄPTER 1: +It was a dark and blinky night; the rain fell in torrents -- except at +occasional intervals, when it was checked by a violent gust of wind which +swept up the streets (for it is in London that our scene lies), rattling along +the housetops, and fiercely agitating the scanty flame of the lamps that +struggled against the elements. A hooded figure emerged. + +It was a Domo-Kun. + +"What took you so long?", inquired his wife. + +Silence. Oblivious to his silence, she continued, "Did Mr. Usagi enjoy the +waffles you brought him?" "You know him, he's not one to forego a waffle, +no matter how burnt," he snickered. + +The pause was filled with the sound of compile errors. + +CHAPTER 2: +The jelly was as dark as night, and just as runny. +The Domo-Kun shuddered, remembering the way Mr. Usagi had speared his waffles +with his fork, watching the runny jelly spread and pool across his plate, +like the blood of a dying fawn. "It reminds me of that time --" he started, as +his wife cut in quickly: "-- please. I can't bear to hear it.". A flury of +images coming from the past flowed through his mind. + +"You recall what happened on Mulholland drive?" The ceiling fan rotated slowly +overhead, barely disturbing the thick cigarette smoke. No doubt was left about +when the fan was last cleaned. + +There was a poignant pause. + +CHAPTER 3: +Mr. Usagi felt that something wasn't right. Shortly after the Domo-Kun left he +began feeling sick. He thought out loud to himself, "No, he wouldn't have done +that to me." He considered that perhaps he shouldn't have pushed so hard. +Perhaps he shouldn't have been so cold and sarcastic, after the unimaginable +horror that had occurred just the week before. + +Next time, there won't be any sushi. Why sushi with waffles anyway? It's like +adorning breakfast cereal with halibut -- shameful. + +CHAPTER 4: +The taste of stale sushi in his mouth the next morning was unbearable. He +wondered where the sushi came from as he attempted to wash the taste away with +a bottle of 3000Â¥ sake. He tries to recall the cook's face. Purple? Probably. + +CHAPTER 5: +Many tears later, Mr. Usagi would laugh at the memory of the earnest, +well-intentioned Domo-Kun. Another day in the life. That is when he realized that +life goes on. + +TRUISMS (1978-1983) +JENNY HOLZER +A LITTLE KNOWLEDGE CAN GO A LONG WAY +A LOT OF PROFESSIONALS ARE CRACKPOTS +A MAN CAN'T KNOW WHAT IT IS TO BE A MOTHER +A NAME MEANS A LOT JUST BY ITSELF +A POSITIVE ATTITUDE MEANS ALL THE DIFFERENCE IN THE WORLD +A RELAXED MAN IS NOT NECESSARILY A BETTER MAN +NO ONE SHOULD EVER USE SVN +AN INFLEXIBLE POSITION SOMETIMES IS A SIGN OF PARALYSIS +IT IS MANS FATE TO OUTSMART HIMSELF +BEING SURE OF YOURSELF MEANS YOU'RE A FOOL +AM NOT +ARE TOO +IF AT FIRST YOU DON'T SUCCEED: TRY, EXCEPT, FINALLY +AND THEN, TIME LEAPT BACKWARDS +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAaaaaaaaaaaaaaaaaaaaaaaaaaaaahhhh LOT +I'm really tempted to change something above the line. +Reeccciiiipppppeeeeeesssssss!!!!!!!!! +PEOPLE SAY "FAILURE IS NOT AN OPTION", BUT FAILURE IS ALWAYS AN OPTION. +WHAT GOES UP MUST HAVE A NON-ZERO VELOCITY + +I can feel the heat closing in, feel them out there making their moves... +What could possibly go wrong? We've already ate our cake. + +Stand Still. Pause Clocks. We can make the World Stop. +WUBWUBWUBWUBWUB + +I want a 1917 build and you will give me what I want. + +This sentence is false. + +Beauty is in the eyes of a Beholder. + +I'm the best at space. + +The first time Yossarian saw the chaplain, he fell madly in love with him. +* +* +* +Give not thyself up, then, to fire, lest it invert thee, deaden thee; as for +the time it did me. There is a wisdom that is woe; but there is a woe that is +madness. And there is a Catskill eagle in some souls that can alike dive down +into the blackest gorges, and soar out of them again and become invisible in +the sunny spaces. And even if he for ever flies within the gorge, that gorge +is in the mountains; so that even in his lowest swoop the mountain eagle is +still higher than other birds upon the plain, even though they soar. +* +* +* + +I'm here to commit lines and drop rhymes +* +This is a line to test and try uploading a cl. + +And lo, in the year 2014, there was verily an attempt to upgrade to GCC 4.8 on +the Android bots, and it was good. Except on one bot, where it was bad. And +lo, the change was reverted, and GCC went back to 4.6, where code is slower +and less optimized. And verily did it break the build, because artifacts had +been created with 4.8, and alignment was no longer the same, and a great +sadness descended upon the Android GN buildbot, and it did refuseth to build +any more. But the sheriffs thought to themselves: Placebo! Let us clobber the +bot, and perhaps it will rebuild with GCC 4.6, which hath worked for many many +seasons. And so they modified the whitespace file with these immortal lines, +and visited it upon the bots, that great destruction might be wrought upon +their outdated binaries. In clobberus, veritas. + +As the git approaches, light begins to shine through the SCM thrice again... +However, the git, is, after all, quite stupid. + +Suddenly Domo-Kun found itself in a room filled with dazzling mirrors. + +A herd of wild gits appears! Time for CQ :D +And one more for sizes.py... + +Sigh. + +It was love at first sight. The moment Yossarian first laid eyes on the chaplain, he fell madly in love with him. + +Cool whitespace change for git-cl land + +Oh god the bots are red! I'm blind! Mmmm, cronuts. + +If you stand on your head, you will get footprints in your hair. + +sigh +sigher +pick up cls + +In the BUILD we trust. +^_^ + +In the masters we don't. diff --git a/engine/src/flutter/build/win_is_xtree_patched.py b/engine/src/flutter/build/win_is_xtree_patched.py new file mode 100755 index 0000000000..3f1994ff9d --- /dev/null +++ b/engine/src/flutter/build/win_is_xtree_patched.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +"""Determines if the VS xtree header has been patched to disable C4702.""" + +import os + + +def IsPatched(): + # TODO(scottmg): For now, just return if we're using the packaged toolchain + # script (because we know it's patched). Another case could be added here to + # query the active VS installation and actually check the contents of xtree. + # http://crbug.com/346399. + return int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', 1)) == 1 + + +def DoMain(_): + """Hook to be called from gyp without starting a separate python + interpreter.""" + return "1" if IsPatched() else "0" + + +if __name__ == '__main__': + print DoMain([]) diff --git a/engine/src/flutter/mojom/BUILD.gn b/engine/src/flutter/mojom/BUILD.gn new file mode 100644 index 0000000000..d26d44ccb8 --- /dev/null +++ b/engine/src/flutter/mojom/BUILD.gn @@ -0,0 +1,43 @@ +# Copyright 2015 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. + +import("//testing/test.gni") + +group("mojom") { + testonly = true + deps = [ + ":tests", + ] +} + +group("tests") { + testonly = true + deps = [ + ":lexer_unittest", + ] +} + +test("lexer_unittest") { + sources = [ + "lexer_unittest.cc", + ] + + deps = [ + "//base", + "//base/test:run_all_unittests", + "//testing/gtest", + ":lexer", + ] +} + +source_set("lexer") { + sources = [ + "lexer.cc", + "lexer.h", + ] + + deps = [ + "//base", + ] +} diff --git a/engine/src/flutter/mojom/lexer.cc b/engine/src/flutter/mojom/lexer.cc new file mode 100644 index 0000000000..e55e2fbca0 --- /dev/null +++ b/engine/src/flutter/mojom/lexer.cc @@ -0,0 +1,420 @@ +// Copyright 2015 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. + +#include "mojom/lexer.h" + +#include +#include + +#include "base/lazy_instance.h" + +namespace mojo { +namespace mojom { + +namespace { + +class KeywordsDict { + public: + KeywordsDict(); + + private: + std::map keywords_; + friend std::map& Keywords(); + + DISALLOW_COPY_AND_ASSIGN(KeywordsDict); +}; +static base::LazyInstance g_keywords = LAZY_INSTANCE_INITIALIZER; + +std::map& Keywords() { + return g_keywords.Get().keywords_; +} + +KeywordsDict::KeywordsDict() { + keywords_["import"] = TokenType::IMPORT; + keywords_["module"] = TokenType::MODULE; + keywords_["struct"] = TokenType::STRUCT; + keywords_["union"] = TokenType::UNION; + keywords_["interface"] = TokenType::INTERFACE; + keywords_["enum"] = TokenType::ENUM; + keywords_["const"] = TokenType::CONST; + keywords_["true"] = TokenType::TRUE; + keywords_["false"] = TokenType::FALSE; + keywords_["default"] = TokenType::DEFAULT; +} + +// Non-localized versions of isalpha. +bool IsAlpha(char c) { + return (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')); +} + +// Non-localized versions of isnum. +bool IsDigit(char c) { + return ('0' <= c && c <= '9'); +} + +bool IsHexDigit(char c) { + return (IsDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')); +} + +// Non-localized versions of isalnum. +bool IsAlnum(char c) { + return IsAlpha(c) || IsDigit(c); +} + +// MojomLexer tokenizes a mojom source file. It is NOT thread-safe. +class MojomLexer { + public: + explicit MojomLexer(const std::string& source); + ~MojomLexer(); + + // Returns the list of tokens in the source file. + std::vector Tokenize(); + + private: + // The GetNextToken.* functions all return true if they could find a token + // (even an error token) and false otherwise. + bool GetNextToken(Token* result); + bool GetNextTokenSingleChar(Token* result); + bool GetNextTokenEqualsOrResponse(Token* result); + bool GetNextTokenIdentifier(Token* result); + bool GetNextTokenDecConst(Token* result); + bool GetNextTokenHexConst(Token* result); + bool GetNextTokenOrdinal(Token* result); + bool GetNextTokenStringLiteral(Token* result); + + void ConsumeSkippable(); + void ConsumeDigits(); + void ConsumeEol(); + void Consume(size_t num); + + bool eos(size_t offset_plus) { + return offset_ + offset_plus >= source_.size(); + } + + const std::string source_; + size_t offset_; + size_t line_no_; + size_t offset_in_line_; + + DISALLOW_COPY_AND_ASSIGN(MojomLexer); +}; + +std::vector MojomLexer::Tokenize() { + offset_ = 0; + line_no_ = 0; + offset_in_line_ = 0; + + std::vector result; + Token cur; + while (GetNextToken(&cur)) { + result.push_back(cur); + + // As soon as an error token is found, stop tokenizing. + if (cur.error()) { + break; + } + } + + return result; +} + +bool MojomLexer::GetNextToken(Token* result) { + // Skip all spaces which may be in front of the next token. + ConsumeSkippable(); + + // If we found the end of the source signal that is so. + if (eos(0)) + return false; + + // Save the current position in the source code. + result->char_pos = offset_; + result->line_no = line_no_; + result->line_pos = offset_in_line_; + + if (GetNextTokenSingleChar(result) || GetNextTokenEqualsOrResponse(result) || + GetNextTokenIdentifier(result) || GetNextTokenHexConst(result) || + GetNextTokenDecConst(result) || GetNextTokenDecConst(result) || + GetNextTokenOrdinal(result) || GetNextTokenStringLiteral(result)) + return true; + + result->token = source_.substr(offset_, 1); + result->token_type = TokenType::ERROR_ILLEGAL_CHAR; + return true; +} + +void MojomLexer::ConsumeSkippable() { + if (eos(0)) + return; + + bool found_non_space = false; + while (!found_non_space && !eos(0)) { + switch (source_[offset_]) { + case ' ': + case '\t': + case '\r': + Consume(1); + break; + case '\n': + ConsumeEol(); + break; + default: + found_non_space = true; + break; + } + } +} + +// Finds all single-character tokens except for '='. +bool MojomLexer::GetNextTokenSingleChar(Token* result) { + switch (source_[offset_]) { + case '(': + result->token_type = TokenType::LPAREN; + break; + case ')': + result->token_type = TokenType::RPAREN; + break; + case '[': + result->token_type = TokenType::LBRACKET; + break; + case ']': + result->token_type = TokenType::RBRACKET; + break; + case '{': + result->token_type = TokenType::LBRACE; + break; + case '}': + result->token_type = TokenType::RBRACE; + break; + case '<': + result->token_type = TokenType::LANGLE; + break; + case '>': + result->token_type = TokenType::RANGLE; + break; + case ';': + result->token_type = TokenType::SEMI; + break; + case ',': + result->token_type = TokenType::COMMA; + break; + case '.': + result->token_type = TokenType::DOT; + break; + case '-': + result->token_type = TokenType::MINUS; + break; + case '+': + result->token_type = TokenType::PLUS; + break; + case '&': + result->token_type = TokenType::AMP; + break; + case '?': + result->token_type = TokenType::QSTN; + break; + default: + return false; + break; + } + + result->token = source_.substr(offset_, 1); + Consume(1); + return true; +} + +// Finds '=' or '=>'. +bool MojomLexer::GetNextTokenEqualsOrResponse(Token* result) { + if (source_[offset_] != '=') + return false; + Consume(1); + + if (eos(0) || source_[offset_] != '>') { + result->token_type = TokenType::EQUALS; + result->token = "="; + } else { + result->token_type = TokenType::RESPONSE; + result->token = "=>"; + Consume(1); + } + return true; +} + +// valid C identifiers (K&R2: A.2.3) +bool MojomLexer::GetNextTokenIdentifier(Token* result) { + char c = source_[offset_]; + + // Identifiers start with a letter or underscore. + if (!(IsAlpha(c) || c == '_')) + return false; + size_t start_offset = offset_; + + // Identifiers contain letters numbers and underscores. + while (!eos(0) && (IsAlnum(source_[offset_]) || c == '_')) + Consume(1); + + result->token = source_.substr(start_offset, offset_ - start_offset); + result->token_type = TokenType::IDENTIFIER; + + if (Keywords().count(result->token)) + result->token_type = Keywords()[result->token]; + + return true; +} + +// integer constants (K&R2: A.2.5.1) dec +// floating constants (K&R2: A.2.5.3) +bool MojomLexer::GetNextTokenDecConst(Token* result) { + if (!IsDigit(source_[offset_])) + return false; + + result->token_type = TokenType::INT_CONST_DEC; + // If the number starts with a zero and is not a floating point number. + if (source_[offset_] == '0' && + (eos(1) || (source_[offset_] == 'e' && source_[offset_] == 'E' && + source_[offset_] == '.'))) { + // TODO(azani): Catch and error on octal. + result->token = "0"; + Consume(1); + return true; + } + + size_t start_offset = offset_; + + // First, we consume all the digits. + ConsumeDigits(); + + // If there is a fractional part, we consume the . and the following digits. + if (!eos(0) && source_[offset_] == '.') { + result->token_type = TokenType::FLOAT_CONST; + Consume(1); + ConsumeDigits(); + } + + // If there is an exponential part, we consume the e and the following digits. + if (!eos(0) && (source_[offset_] == 'e' || source_[offset_] == 'E')) { + if (!eos(2) && (source_[offset_ + 1] == '-' || source_[offset_ + 1]) && + IsDigit(source_[offset_ + 2])) { + result->token_type = TokenType::FLOAT_CONST; + Consume(2); // Consume e/E and +/- + ConsumeDigits(); + } else if (!eos(1) && IsDigit(source_[offset_ + 1])) { + result->token_type = TokenType::FLOAT_CONST; + Consume(1); // Consume e/E + ConsumeDigits(); + } + } + + result->token = source_.substr(start_offset, offset_ - start_offset); + return true; +} + +// integer constants (K&R2: A.2.5.1) hex +bool MojomLexer::GetNextTokenHexConst(Token* result) { + // Hex numbers start with a 0, x and then some hex numeral. + if (eos(2) || source_[offset_] != '0' || + (source_[offset_ + 1] != 'x' && source_[offset_ + 1] != 'X') || + !IsHexDigit(source_[offset_ + 2])) + return false; + + result->token_type = TokenType::INT_CONST_HEX; + size_t start_offset = offset_; + Consume(2); + + while (IsHexDigit(source_[offset_])) + Consume(1); + + result->token = source_.substr(start_offset, offset_ - start_offset); + return true; +} + +bool MojomLexer::GetNextTokenOrdinal(Token* result) { + // Ordinals start with '@' and then some digit. + if (eos(1) || source_[offset_] != '@' || !IsDigit(source_[offset_ + 1])) + return false; + size_t start_offset = offset_; + // Consumes '@'. + Consume(1); + + result->token_type = TokenType::ORDINAL; + ConsumeDigits(); + + result->token = source_.substr(start_offset, offset_ - start_offset); + return true; +} + +bool MojomLexer::GetNextTokenStringLiteral(Token* result) { + // Ordinals start with '@' and then some digit. + if (source_[offset_] != '"') + return false; + + size_t start_offset = offset_; + // Consumes '"'. + Consume(1); + + while (source_[offset_] != '"') { + if (source_[offset_] == '\n' || eos(0)) { + result->token_type = TokenType::ERROR_UNTERMINATED_STRING_LITERAL; + result->token = source_.substr(start_offset, offset_ - start_offset); + return true; + } + + // This block will be skipped if the backslash is at the end of the source. + if (source_[offset_] == '\\' && !eos(1)) { + // Consume the backslash. This will ensure \" is consumed. + Consume(1); + } + Consume(1); + } + // Consume the closing doublequotes. + Consume(1); + + result->token_type = TokenType::STRING_LITERAL; + + result->token = source_.substr(start_offset, offset_ - start_offset); + return true; +} + +void MojomLexer::ConsumeDigits() { + while (!eos(0) && IsDigit(source_[offset_])) + Consume(1); +} + +void MojomLexer::ConsumeEol() { + ++offset_; + ++line_no_; + offset_in_line_ = 0; +} + +void MojomLexer::Consume(size_t num) { + offset_ += num; + offset_in_line_ += num; +} + +MojomLexer::MojomLexer(const std::string& source) + : source_(source), offset_(0), line_no_(0), offset_in_line_(0) { +} + +MojomLexer::~MojomLexer() { +} + +} // namespace + +Token::Token() + : token_type(TokenType::ERROR_UNKNOWN), + char_pos(0), + line_no(0), + line_pos(0) { +} + +Token::~Token() { +} + +// Accepts the text of a mojom file and returns the ordered list of tokens +// found in the file. +std::vector Tokenize(const std::string& source) { + return MojomLexer(source).Tokenize(); +} + +} // namespace mojom +} // namespace mojo diff --git a/engine/src/flutter/mojom/lexer.h b/engine/src/flutter/mojom/lexer.h new file mode 100644 index 0000000000..b477a37107 --- /dev/null +++ b/engine/src/flutter/mojom/lexer.h @@ -0,0 +1,92 @@ +// Copyright 2015 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. + +#ifndef MOJO_PUBLIC_TOOLS_BINDINGS_MOJOM_CPP_LEXER_H_ +#define MOJO_PUBLIC_TOOLS_BINDINGS_MOJOM_CPP_LEXER_H_ + +#include +#include +#include + +#include "base/macros.h" + +namespace mojo { +namespace mojom { + +enum class TokenType { + // Errors + ERROR_UNKNOWN, + ERROR_ILLEGAL_CHAR, + ERROR_UNTERMINATED_STRING_LITERAL, + + // Punctuators and Separators + LPAREN, + RPAREN, + LBRACKET, + RBRACKET, + LBRACE, + RBRACE, + LANGLE, + RANGLE, + SEMI, + COMMA, + DOT, + MINUS, + PLUS, + AMP, + QSTN, + EQUALS, + RESPONSE, + + // Identifiers + IDENTIFIER, + + // Keywords + IMPORT, + MODULE, + STRUCT, + UNION, + INTERFACE, + ENUM, + CONST, + TRUE, + FALSE, + DEFAULT, + + // Constants + INT_CONST_DEC, + INT_CONST_HEX, + FLOAT_CONST, + ORDINAL, + STRING_LITERAL, + + // TODO(azani): Check that all tokens were implemented. + // TODO(azani): Error out on octal. +}; + +struct Token { + Token(); + ~Token(); + + bool error() const { + return (token_type == TokenType::ERROR_ILLEGAL_CHAR || + token_type == TokenType::ERROR_UNTERMINATED_STRING_LITERAL || + token_type == TokenType::ERROR_UNKNOWN); + } + + TokenType token_type; + std::string token; + size_t char_pos; + size_t line_no; + size_t line_pos; +}; + +// Accepts the text of a mojom file and returns the ordered list of tokens +// found in the file. +std::vector Tokenize(const std::string& source); + +} // namespace mojom +} // namespace mojo + +#endif // MOJO_PUBLIC_TOOLS_BINDINGS_MOJOM_CPP_LEXER_H_ diff --git a/engine/src/flutter/mojom/lexer_unittest.cc b/engine/src/flutter/mojom/lexer_unittest.cc new file mode 100644 index 0000000000..f4db79a6e9 --- /dev/null +++ b/engine/src/flutter/mojom/lexer_unittest.cc @@ -0,0 +1,162 @@ +// Copyright 2015 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. + +#include "base/logging.h" +#include "mojom/lexer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace mojom { +namespace { + +TEST(LexerTest, AllNonErrorTokens) { + const struct TestData { + const char* name; + const char* source; + mojom::TokenType expected_token; + } test_data[] = { + {"LPAREN", "(", mojom::TokenType::LPAREN}, + {"RPAREN", ")", mojom::TokenType::RPAREN}, + {"LBRACKET", "[", mojom::TokenType::LBRACKET}, + {"RBRACKET", "]", mojom::TokenType::RBRACKET}, + {"LBRACE", "{", mojom::TokenType::LBRACE}, + {"RBRACE", "}", mojom::TokenType::RBRACE}, + {"LANGLE", "<", mojom::TokenType::LANGLE}, + {"RANGLE", ">", mojom::TokenType::RANGLE}, + {"SEMI", ";", mojom::TokenType::SEMI}, + {"COMMA", ",", mojom::TokenType::COMMA}, + {"DOT", ".", mojom::TokenType::DOT}, + {"MINUS", "-", mojom::TokenType::MINUS}, + {"PLUS", "+", mojom::TokenType::PLUS}, + {"AMP", "&", mojom::TokenType::AMP}, + {"QSTN", "?", mojom::TokenType::QSTN}, + {"EQUALS", "=", mojom::TokenType::EQUALS}, + {"RESPONSE", "=>", mojom::TokenType::RESPONSE}, + {"IDENTIFIER", "something", mojom::TokenType::IDENTIFIER}, + {"IMPORT", "import", mojom::TokenType::IMPORT}, + {"MODULE", "module", mojom::TokenType::MODULE}, + {"STRUCT", "struct", mojom::TokenType::STRUCT}, + {"UNION", "union", mojom::TokenType::UNION}, + {"INTERFACE", "interface", mojom::TokenType::INTERFACE}, + {"ENUM", "enum", mojom::TokenType::ENUM}, + {"CONST", "const", mojom::TokenType::CONST}, + {"TRUE", "true", mojom::TokenType::TRUE}, + {"FALSE", "false", mojom::TokenType::FALSE}, + {"DEFAULT", "default", mojom::TokenType::DEFAULT}, + {"INT_CONST_DEC", "10", mojom::TokenType::INT_CONST_DEC}, + {"INT_CONST_DEC_0", "0", mojom::TokenType::INT_CONST_DEC}, + {"FLOAT_CONST", "10.5", mojom::TokenType::FLOAT_CONST}, + {"FLOAT_CONST_E", "10e5", mojom::TokenType::FLOAT_CONST}, + {"FLOAT_CONST_ZERO", "0.5", mojom::TokenType::FLOAT_CONST}, + {"FLOAT_CONST_E_ZERO", "0e5", mojom::TokenType::FLOAT_CONST}, + {"FLOAT_CONST_E_PLUS", "10e+5", mojom::TokenType::FLOAT_CONST}, + {"FLOAT_CONST_E_MINUS", "10e-5", mojom::TokenType::FLOAT_CONST}, + {"INT_CONST_HEX", "0x10A", mojom::TokenType::INT_CONST_HEX}, + {"ORDINAL", "@10", mojom::TokenType::ORDINAL}, + {"STRING_LITERAL", "\"hello world\"", mojom::TokenType::STRING_LITERAL}, + {"STRING_LITERAL_ESCAPE", + "\"hello \\\"world\\\"\"", + mojom::TokenType::STRING_LITERAL}, + {"STRING_LITERAL_HEX_ESCAPE", + "\"hello \\x23 world\"", + mojom::TokenType::STRING_LITERAL}, + }; + for (size_t i = 0; i < arraysize(test_data); i++) { + const char* test_name = test_data[i].name; + const char* source = test_data[i].source; + const mojom::TokenType expected_token = test_data[i].expected_token; + std::vector tokens = mojom::Tokenize(source); + DCHECK(tokens.size() >= 1) << "Failure to tokenize at all: " << test_name; + const mojom::Token token = tokens[0]; + EXPECT_EQ(expected_token, token.token_type) + << "Wrong token type: " << test_name; + EXPECT_EQ(source, token.token) << "Wrong token value: " << test_name; + } +} + +TEST(LexerTest, TokenPosition) { + std::string source(" \n ."); + std::vector tokens = mojom::Tokenize(source); + const mojom::Token token = tokens[0]; + EXPECT_EQ(mojom::TokenType::DOT, token.token_type); + EXPECT_EQ(".", token.token); + EXPECT_EQ(5U, token.char_pos); + EXPECT_EQ(1U, token.line_no); + EXPECT_EQ(2U, token.line_pos); +} + +TEST(LexerTest, ExhaustedTokens) { + std::string source(""); + std::vector tokens = mojom::Tokenize(source); + EXPECT_EQ(0U, tokens.size()); +} + +TEST(LexerTest, SkipSkippable) { + std::string source(" \t \r \n ."); + std::vector tokens = mojom::Tokenize(source); + const mojom::Token token = tokens[0]; + EXPECT_EQ(mojom::TokenType::DOT, token.token_type); + EXPECT_EQ(".", token.token); +} + +TEST(LexerTest, SkipToTheEnd) { + std::string source(" \t \r \n "); + std::vector tokens = mojom::Tokenize(source); + EXPECT_EQ(0U, tokens.size()); +} + +TEST(LexerTest, TokenizeMoreThanOne) { + std::string source("()"); + std::vector tokens = mojom::Tokenize(source); + + EXPECT_EQ(mojom::TokenType::LPAREN, tokens[0].token_type); + EXPECT_EQ(mojom::TokenType::RPAREN, tokens[1].token_type); + EXPECT_EQ(2U, tokens.size()); +} + +TEST(LexerTest, ERROR_ILLEGAL_CHAR) { + std::string source("#"); + std::vector tokens = mojom::Tokenize(source); + const mojom::Token token = tokens[0]; + EXPECT_EQ(mojom::TokenType::ERROR_ILLEGAL_CHAR, token.token_type); + EXPECT_EQ("#", token.token); + EXPECT_TRUE(token.error()); +} + +TEST(LexerTest, ERROR_UNTERMINATED_STRING_LITERAL_EOL) { + std::string source("\"Hello \n World\""); + std::vector tokens = mojom::Tokenize(source); + const mojom::Token token = tokens[0]; + EXPECT_EQ(mojom::TokenType::ERROR_UNTERMINATED_STRING_LITERAL, + token.token_type); + EXPECT_EQ("\"Hello ", token.token); + EXPECT_EQ(0U, token.char_pos); + EXPECT_TRUE(token.error()); +} + +TEST(LexerTest, ERROR_UNTERMINATED_STRING_LITERAL_EOF) { + std::string source("\"Hello "); + std::vector tokens = mojom::Tokenize(source); + const mojom::Token token = tokens[0]; + EXPECT_EQ(mojom::TokenType::ERROR_UNTERMINATED_STRING_LITERAL, + token.token_type); + EXPECT_EQ("\"Hello ", token.token); + EXPECT_EQ(0U, token.char_pos); + EXPECT_TRUE(token.error()); +} + +TEST(LexerTest, ERROR_UNTERMINATED_STRING_LITERAL_ESC_EOF) { + std::string source("\"Hello \\"); + std::vector tokens = mojom::Tokenize(source); + const mojom::Token token = tokens[0]; + EXPECT_EQ(mojom::TokenType::ERROR_UNTERMINATED_STRING_LITERAL, + token.token_type); + EXPECT_EQ("\"Hello \\", token.token); + EXPECT_EQ(0U, token.char_pos); + EXPECT_TRUE(token.error()); +} + +} // namespace +} // namespace mojom +} // namespace mojo diff --git a/engine/src/flutter/mojom/mojom.ebnf b/engine/src/flutter/mojom/mojom.ebnf new file mode 100644 index 0000000000..987e54e5a9 --- /dev/null +++ b/engine/src/flutter/mojom/mojom.ebnf @@ -0,0 +1,63 @@ +(* Copyright 2015 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. *) + +(* mojom_file is the root production rule. *) +mojom_file = [ module_decl ] , { import_stmt } , { primary_object_decl } ; +module_decl = attribute_section , "module" , identifier , ";" ; +import_stmt = "import" , STRING_LITERAL , ";" ; +primary_object_decl = interface_decl | struct_decl | union_decl | enum_decl | const_decl ; +attribute_section = [ "[" , attribute_list , "]" ] ; +attribute_list = [ attribute_assignment , { "," , attribute_assignment } ] ; +attribute_assignment = NAME , "=" , ( NAME | literal ) ; +interface_decl = attribute_section , "interface" , NAME , "{" , interface_body , "}" , ";" ; +interface_body = { method_decl | enum_decl | const_decl } ; +method_decl = attribute_section , NAME , [ ORDINAL ] , "(" , parameter_list , ")" , response_decl , ";" ; +response_decl = [ "=>" , "(" , parameter_list , ")" ] ; +parameter_list = [ parameter_decl { "," , parameter_decl } ] ; +parameter_decl = attribute_section , typespec , NAME , [ ORDINAL ] ; +struct_decl = attribute_section , "struct" , NAME , "{" , struct_body , "}" , ";" ; +struct_body = { struct_field_decl | enum_decl | const_decl } ; +struct_field_decl = attribute_section , typespec , NAME , [ ORDINAL ] , default_value_assignment , ";" ; +union_decl = attribute_section , "union" , NAME , "{" , union_body , "}" , ";" ; +union_body = { union_field_decl } ; +union_field_decl = attribute_section , typespec , NAME , [ ORDINAL ] , ";" ; +default_value_assignment = [ "=" , constant ] ; +enum_decl = attribute_section , "enum" , NAME , "{" , enum_value_list , [ "," ] , "}" , ";" ; +enum_value_list = enum_value , { "," , enum_value } ; +enum_value = attribute_section , NAME , [ "=" , ( identifier | int_literal ) ] ; +const_decl = "const" , typespec , NAME , "=" , constant , ";" ; +typespec = nonnullable_typespec , [ "?" ] ; +nonnullable_typespec = basictypespec | map_typespec | array_typespec + | fixed_array_typespec | interface_request_typespec ; +basictypespec = handle_typespec | identifier ; +handle_typespec = "handle" , [ "<" , NAME , ">" ] ; +array_typespec = "array" , "<" , typespec , ">" ; +fixed_array_typespec = "array" , "<" , typespec , "," , INT_CONST_DEC , ">" ; +map_typespec = "map" , "<" , identifier , "," , typespec , ">" ; +interface_request_typespec = identifier , "&" ; +constant = identifier | literal ; +identifier = NAME , [ "." , identifier ] ; +literal = "default" | "true" | "false" | int_literal + | float_literal | STRING_LITERAL ; +float_literal = [ "-" | "+" ] , FLOAT_CONST ; +int_literal = [ "-" | "+" ] , int_const ; +int_const = INT_CONST_DEC | INT_CONST_HEX ; + +(* Capitalized names are terminals expressed as regular expressions. *) +ORDINAL = @[0-9]+ + +(* character constants (K&R2: A.2.5.2) *) +STRING_LITERAL = "([^"\\\n]|(\\(([a-zA-Z._~!=&\^\-\\?'"])|(\d+)|(x[0-9a-fA-F]+))))*" + +(* integer constants (K&R2: A.2.5.1) *) +INT_CONST_HEX = 0[xX][0-9a-fA-F]+ +INT_CONST_DEC = 0|([1-9][0-9]*) + +(* floating constants (K&R2: A.2.5.3) *) +FLOAT_CONST = ((((([0-9]*\.[0-9]+)|([0-9]+\.))([eE][-+]?[0-9]+)?)|([0-9]+([eE][-+]?[0-9]+)))) + +(* valid C identifiers (K&R2: A.2.3) + Some names are reserved. That is indicated by their literal use in the grammar + above. *) +NAME = [a-zA-Z_][0-9a-zA-Z_]* diff --git a/engine/src/flutter/skia/BUILD.gn b/engine/src/flutter/skia/BUILD.gn new file mode 100644 index 0000000000..c226044ff1 --- /dev/null +++ b/engine/src/flutter/skia/BUILD.gn @@ -0,0 +1,658 @@ +# 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. + +import("//build/config/features.gni") +import("//build/config/ui.gni") +import("//testing/test.gni") +if (current_cpu == "arm") { + import("//build/config/arm.gni") +} +if (current_cpu == "mipsel" || current_cpu == "mips64el") { + import("//build/config/mips.gni") +} + +# The list of Skia defines that are to be set for chromium. +gypi_skia_defines = + exec_script("//build/gypi_to_gn.py", + [ + rebase_path( + "//third_party/skia/gyp/skia_for_chromium_defines.gypi"), + "--replace=<(skia_include_path)=//third_party/skia/include", + "--replace=<(skia_src_path)=//third_party/skia/src", + ], + "scope", + [ "//third_party/skia/gyp/skia_for_chromium_defines.gypi" ]) + +# The list of Skia core sources that are to be set for chromium. +gypi_skia_core = + exec_script("//build/gypi_to_gn.py", + [ + rebase_path("//third_party/skia/gyp/core.gypi"), + "--replace=<(skia_include_path)=//third_party/skia/include", + "--replace=<(skia_src_path)=//third_party/skia/src", + ], + "scope", + [ "//third_party/skia/gyp/core.gypi" ]) + +# The list of Skia gpu sources that are to be set for chromium. +gypi_skia_gpu = + exec_script("//build/gypi_to_gn.py", + [ + rebase_path("//third_party/skia/gyp/gpu.gypi"), + "--replace=<(skia_include_path)=//third_party/skia/include", + "--replace=<(skia_src_path)=//third_party/skia/src", + ], + "scope", + [ "//third_party/skia/gyp/gpu.gypi" ]) + +# The list of Skia effects that are to be set for chromium. +gypi_skia_effects = + exec_script("//build/gypi_to_gn.py", + [ + rebase_path("//third_party/skia/gyp/effects.gypi"), + "--replace=<(skia_include_path)=//third_party/skia/include", + "--replace=<(skia_src_path)=//third_party/skia/src", + ], + "scope", + [ "//third_party/skia/gyp/effects.gypi" ]) + +# The list of Skia utils that are to be set for chromium. +gypi_skia_utils = + exec_script("//build/gypi_to_gn.py", + [ + rebase_path("//third_party/skia/gyp/utils.gypi"), + "--replace=<(skia_include_path)=//third_party/skia/include", + "--replace=<(skia_src_path)=//third_party/skia/src", + ], + "scope", + [ "//third_party/skia/gyp/utils.gypi" ]) + +gypi_skia_opts = + exec_script("//build/gypi_to_gn.py", + [ + rebase_path("//third_party/skia/gyp/opts.gypi"), + "--replace=<(skia_include_path)=//third_party/skia/include", + "--replace=<(skia_src_path)=//third_party/skia/src", + ], + "scope", + [ "//third_party/skia/gyp/opts.gypi" ]) + +# The list of Skia files is kept in skia_gn_files.gypi. Read it. +gypi_values = + exec_script("//build/gypi_to_gn.py", + [ + rebase_path("skia_gn_files.gypi"), + "--replace=<(skia_include_path)=//third_party/skia/include", + "--replace=<(skia_src_path)=//third_party/skia/src", + ], + "scope", + [ "skia_gn_files.gypi" ]) + +# External-facing config for dependent code. +config("skia_config") { + include_dirs = [ + "config", + "ext", + "//third_party/skia/include/c", + "//third_party/skia/include/config", + "//third_party/skia/include/core", + "//third_party/skia/include/effects", + "//third_party/skia/include/images", + "//third_party/skia/include/lazy", + "//third_party/skia/include/pathops", + "//third_party/skia/include/pdf", + "//third_party/skia/include/pipe", + "//third_party/skia/include/ports", + "//third_party/skia/include/utils", + ] + + defines = gypi_skia_defines.skia_for_chromium_defines + + defines += [] + + if (component_mode == "shared_library") { + defines += [ + "SKIA_DLL", + "GR_GL_IGNORE_ES3_MSAA=0", + ] + } + + include_dirs += [ + "//third_party/skia/include/gpu", + "//third_party/skia/src/gpu", + ] + defines += [ "SK_SUPPORT_GPU=1" ] + + if (is_android) { + defines += [ + "SK_BUILD_FOR_ANDROID", + "USE_CHROMIUM_SKIA", + ] + } + + if (is_mac) { + defines += [ "SK_BUILD_FOR_MAC" ] + } + + if (is_ios) { + defines += [ "SK_BUILD_FOR_IOS" ] + } +} + +# Internal-facing config for Skia library code. +config("skia_library_config") { + # These include directories are only included for Skia code and are not + # exported to dependents. It's not clear if this is on purpose, but this + # matches the GYP build. + include_dirs = [ + "//third_party/skia/src/core", + "//third_party/skia/src/image", + "//third_party/skia/src/opts", + "//third_party/skia/src/pdf", + "//third_party/skia/src/ports", + "//third_party/skia/src/sfnt", + "//third_party/skia/src/utils", + "//third_party/skia/src/lazy", + ] + if (is_mac || is_ios) { + include_dirs += [ "//third_party/skia/include/utils/mac" ] + } + if (is_mac) { + include_dirs += [ "//third_party/skia/include/utils/ios" ] + } + + defines = [] + + if (component_mode == "shared_library") { + defines += [ "SKIA_IMPLEMENTATION=1" ] + } + + if (current_cpu == "arm") { + if (arm_use_neon) { + defines += [ "SK_ARM_HAS_NEON" ] + } + if (arm_optionally_use_neon) { + defines += [ "SK_ARM_HAS_OPTIONAL_NEON" ] + } + } + + # Settings for text blitting, chosen to approximate the system browser. + if (is_linux) { + defines += [ + "SK_GAMMA_EXPONENT=1.2", + "SK_GAMMA_CONTRAST=0.2", + "SK_HIGH_QUALITY_IS_LANCZOS", + ] + } else if (is_android) { + defines += [ + "SK_GAMMA_APPLY_TO_A8", + "SK_GAMMA_EXPONENT=1.4", + "SK_GAMMA_CONTRAST=0.0", + ] + } else if (is_win) { + defines += [ + "SK_GAMMA_SRGB", + "SK_GAMMA_CONTRAST=0.5", + "SK_HIGH_QUALITY_IS_LANCZOS", + ] + } else if (is_mac) { + defines += [ + "SK_GAMMA_SRGB", + "SK_GAMMA_CONTRAST=0.0", + "SK_HIGH_QUALITY_IS_LANCZOS", + ] + } + + if (is_android) { + defines += [ + # Android devices are typically more memory constrained, so default to a + # smaller glyph cache (it may be overriden at runtime when the renderer + # starts up, depending on the actual device memory). + "SK_DEFAULT_FONT_CACHE_LIMIT=1048576", # 1024 * 1024 + ] + } else { + defines += [ "SK_DEFAULT_FONT_CACHE_LIMIT=20971520" ] # 20 * 1024 * 1024 + } + + if (is_win) { + include_dirs += [ + "//third_party/skia/include/utils/win", + "//third_party/skia/src/utils/win", + ] + + defines += [ + # On windows, GDI handles are a scarse system-wide resource so we have to + # keep the glyph cache, which holds up to 4 GDI handles per entry, to a + # fairly small size. http://crbug.com/314387 + "SK_DEFAULT_FONT_CACHE_COUNT_LIMIT=256", + ] + + cflags = [ + "/wd4244", # conversion from 'type1( __int64)' to 'type2 (unsigned int)' + "/wd4267", # conversion from 'size_t' (64 bit) to 'type'(32 bit). + "/wd4341", # signed value is out of range for enum constant. + "/wd4345", # Object is default-initialized if initialization is omitted. + "/wd4390", # ';'empty statement found in looping;is it what was intended? + "/wd4554", # 'operator' : check operator precedence for possible error + "/wd4748", # compiler will disable optimizations if a function has inline + # assembly code contains flow control(jmp or jcc) statements. + + "/wd4800", # forcing value to bool 'true/false'(assigning int to bool). + ] + } + + if (is_ios) { + cflags = [ + # Skia uses routines deprecated in iOS 7 and above + "-Wno-deprecated-declarations", + ] + + libs = [ "ImageIO.framework" ] + } +} + +component("skia") { + sources = [ + # Chrome sources. + "config/SkUserConfig.h", + "ext/SkDiscardableMemory_chrome.cc", + "ext/SkDiscardableMemory_chrome.h", + "ext/SkMemory_new_handler.cpp", + "ext/analysis_canvas.cc", + "ext/analysis_canvas.h", + "ext/benchmarking_canvas.cc", + "ext/benchmarking_canvas.h", + "ext/bitmap_platform_device.h", + "ext/convolver.cc", + "ext/convolver.h", + "ext/event_tracer_impl.cc", + "ext/event_tracer_impl.h", + "ext/fontmgr_default_win.cc", + "ext/fontmgr_default_win.h", + "ext/google_logging.cc", + "ext/image_operations.cc", + "ext/image_operations.h", + "ext/opacity_draw_filter.cc", + "ext/opacity_draw_filter.h", + "ext/pixel_ref_utils.cc", + "ext/pixel_ref_utils.h", + "ext/platform_canvas.cc", + "ext/platform_canvas.h", + "ext/platform_device.cc", + "ext/platform_device.h", + "ext/platform_device_linux.cc", + "ext/platform_device_mac.cc", + "ext/platform_device_win.cc", + "ext/recursive_gaussian_convolution.cc", + "ext/recursive_gaussian_convolution.h", + "ext/refptr.h", + "ext/skia_utils_base.cc", + "ext/skia_utils_base.h", + "ext/skia_utils_ios.h", + "ext/skia_utils_ios.mm", + "ext/skia_utils_mac.h", + "ext/skia_utils_mac.mm", + "ext/skia_utils_win.cc", + "ext/skia_utils_win.h", + ] + + # The skia gypi values are relative to the skia_dir, so we need to rebase. + sources += gypi_skia_core.sources + sources += gypi_skia_effects.sources + sources += gypi_skia_utils.sources + sources += gypi_values.skia_library_sources + + # This and skia_opts are really the same conceptual target so share headers. + allow_circular_includes_from = [ ":skia_opts" ] + + if (current_cpu == "arm") { + sources += [ + "//third_party/skia/src/core/SkUtilsArm.cpp", + "//third_party/skia/src/core/SkUtilsArm.h", + ] + } + + # GPU + + sources += gypi_skia_gpu.skgpu_sources + sources += gypi_skia_gpu.skgpu_null_gl_sources + + # Remove unused util files include in utils.gypi + sources -= [ + "//third_party/skia/include/utils/SkBoundaryPatch.h", + "//third_party/skia/include/utils/SkCamera.h", + "//third_party/skia/include/utils/SkCanvasStateUtils.h", + "//third_party/skia/include/utils/SkCubicInterval.h", + "//third_party/skia/include/utils/SkCullPoints.h", + "//third_party/skia/include/utils/SkDebugUtils.h", + "//third_party/skia/include/utils/SkDumpCanvas.h", + "//third_party/skia/include/utils/SkEventTracer.h", + "//third_party/skia/include/utils/SkFrontBufferedStream.h", + "//third_party/skia/include/utils/SkInterpolator.h", + "//third_party/skia/include/utils/SkLayer.h", + "//third_party/skia/include/utils/SkMeshUtils.h", + "//third_party/skia/include/utils/SkNinePatch.h", + "//third_party/skia/include/utils/SkParsePaint.h", + "//third_party/skia/include/utils/SkParsePath.h", + "//third_party/skia/include/utils/SkRandom.h", + "//third_party/skia/src/utils/SkBitmapHasher.cpp", + "//third_party/skia/src/utils/SkBitmapHasher.h", + "//third_party/skia/src/utils/SkBoundaryPatch.cpp", + "//third_party/skia/src/utils/SkCamera.cpp", + "//third_party/skia/src/utils/SkCanvasStack.h", + "//third_party/skia/src/utils/SkCubicInterval.cpp", + "//third_party/skia/src/utils/SkCullPoints.cpp", + "//third_party/skia/src/utils/SkDumpCanvas.cpp", + "//third_party/skia/src/utils/SkFloatUtils.h", + "//third_party/skia/src/utils/SkFrontBufferedStream.cpp", + "//third_party/skia/src/utils/SkInterpolator.cpp", + "//third_party/skia/src/utils/SkLayer.cpp", + "//third_party/skia/src/utils/SkMD5.cpp", + "//third_party/skia/src/utils/SkMD5.h", + "//third_party/skia/src/utils/SkMeshUtils.cpp", + "//third_party/skia/src/utils/SkNinePatch.cpp", + "//third_party/skia/src/utils/SkOSFile.cpp", + "//third_party/skia/src/utils/SkParsePath.cpp", + "//third_party/skia/src/utils/SkSHA1.cpp", + "//third_party/skia/src/utils/SkSHA1.h", + "//third_party/skia/src/utils/SkTFitsIn.h", + "//third_party/skia/src/utils/SkTLogic.h", + + # We don't currently need to change thread affinity, so leave out this complexity for now. + "//third_party/skia/src/utils/SkThreadUtils_pthread_linux.cpp", + "//third_party/skia/src/utils/SkThreadUtils_pthread_mach.cpp", + + #testing + "//third_party/skia/src/fonts/SkGScalerContext.cpp", + "//third_party/skia/src/fonts/SkGScalerContext.h", + ] + + if (is_win) { + sources -= [ + # Keeping _win.cpp + "//third_party/skia/src/utils/SkThreadUtils_pthread.cpp", + "//third_party/skia/src/utils/SkThreadUtils_pthread_other.cpp", + ] + } else { + sources -= [ + # Keeping _pthread.cpp and _pthread_other.cpp. + "//third_party/skia/src/utils/SkThreadUtils_win.cpp", + ] + } + + # need separate win section to handle chromes auto gn filter + # (build/config/BUILDCONFIG.gn) + if (is_win) { + sources -= [ + #windows + "//third_party/skia/include/utils/win/SkAutoCoInitialize.h", + "//third_party/skia/include/utils/win/SkHRESULT.h", + "//third_party/skia/include/utils/win/SkIStream.h", + "//third_party/skia/include/utils/win/SkTScopedComPtr.h", + "//third_party/skia/src/utils/win/SkAutoCoInitialize.cpp", + "//third_party/skia/src/utils/win/SkIStream.cpp", + "//third_party/skia/src/utils/win/SkWGL_win.cpp", + ] + } + + if (is_ios) { + set_sources_assignment_filter([]) + + sources += [ + "//third_party/skia/src/utils/mac/SkCreateCGImageRef.cpp", + "//third_party/skia/src/utils/mac/SkStream_mac.cpp", + ] + + set_sources_assignment_filter(sources_assignment_filter) + } + + if (is_android && (!enable_basic_printing && !enable_print_preview)) { + sources -= [ "ext/skia_utils_base.cc" ] + } + + # Fixup skia library sources. + if (is_win) { + sources -= [ + "//third_party/skia/src/ports/SkOSFile_posix.cpp", + "//third_party/skia/src/ports/SkTLS_pthread.cpp", + "//third_party/skia/src/ports/SkTime_Unix.cpp", + ] + } else { + sources -= [ + "//third_party/skia/src/ports/SkFontHost_win.cpp", + "//third_party/skia/src/ports/SkFontMgr_win_dw.cpp", + "//third_party/skia/src/ports/SkOSFile_win.cpp", + "//third_party/skia/src/ports/SkRemotableFontMgr_win_dw.cpp", + "//third_party/skia/src/ports/SkScalerContext_win_dw.cpp", + "//third_party/skia/src/ports/SkScalerContext_win_dw.h", + "//third_party/skia/src/ports/SkTLS_win.cpp", + "//third_party/skia/src/ports/SkTypeface_win_dw.cpp", + "//third_party/skia/src/ports/SkTypeface_win_dw.h", + ] + } + if (!is_android) { + sources -= [ + "//third_party/skia/src/ports/SkFontMgr_android.cpp", + "//third_party/skia/src/ports/SkFontMgr_android_factory.cpp", + "//third_party/skia/src/ports/SkFontMgr_android_parser.cpp", + ] + } + if (!is_mac && !is_ios) { + sources -= [ "//third_party/skia/src/ports/SkFontHost_mac.cpp" ] + } + + if (!is_linux) { + sources -= [ + "//third_party/skia/src/fonts/SkFontMgr_fontconfig.cpp", + "//third_party/skia/src/ports/SkFontConfigInterface_direct.cpp", + "//third_party/skia/src/ports/SkFontHost_fontconfig.cpp", + ] + } + + if (!is_linux && !is_android) { + sources -= [ + "//third_party/skia/src/ports/SkFontHost_FreeType.cpp", + "//third_party/skia/src/ports/SkFontHost_FreeType_common.cpp", + ] + } + + # Select the right BitmapPlatformDevice. + if (is_win) { + sources += [ + "ext/bitmap_platform_device_win.cc", + "ext/bitmap_platform_device_win.h", + ] + } else if (is_mac || is_ios) { + sources += [ + "ext/bitmap_platform_device_mac.cc", + "ext/bitmap_platform_device_mac.h", + ] + } else if (use_cairo) { + sources += [ + "ext/bitmap_platform_device_cairo.cc", + "ext/bitmap_platform_device_cairo.h", + ] + } else { + sources += [ + "ext/bitmap_platform_device_skia.cc", + "ext/bitmap_platform_device_skia.h", + ] + } + + if (is_clang) { + # Skia won't compile with some of the more strict clang warnings. + # e.g. it does: + # SkASSERT(!"sk_out_of_memory"); + configs -= [ "//build/config/clang:extra_warnings" ] + } + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ + ":skia_library_config", + "//build/config/compiler:no_chromium_code", + ] + public_configs = [ ":skia_config" ] + + deps = [ + ":skia_opts", + "//base", + "//base/third_party/dynamic_annotations", + "//third_party/zlib", + ] + + if (is_linux) { + configs += [ + "//build/config/linux:fontconfig", + "//build/config/linux:freetype2", + ] + if (use_pango) { + configs += [ "//build/config/linux:pangocairo" ] + } + deps += [ "//third_party/icu:icuuc" ] + } + + if (is_android) { + set_sources_assignment_filter([]) + sources += [ "ext/platform_device_linux.cc" ] + set_sources_assignment_filter(sources_assignment_filter) + deps += [ + "//third_party/expat", + "//third_party/freetype-android:freetype", + "//third_party/android_tools:cpu_features", + ] + } + + if (is_android && !is_debug) { + configs -= [ "//build/config/compiler:optimize" ] + configs += [ "//build/config/compiler:optimize_max" ] + } +} + +# Separated out so it can be compiled with different flags for SSE. +source_set("skia_opts") { + cflags = [] + defines = [] + + if (current_cpu == "x86" || current_cpu == "x64") { + sources = gypi_skia_opts.sse2_sources + gypi_skia_opts.ssse3_sources + + gypi_skia_opts.sse41_sources + + [ + # Chrome-specific. + "ext/convolver_SSE2.cc", + "ext/convolver_SSE2.h", + ] + + if (is_linux || is_mac) { + cflags += [ "-msse4.1" ] + } + } else if (current_cpu == "arm") { + # The assembly uses the frame pointer register (r7 in Thumb/r11 in + # ARM), the compiler doesn't like that. + cflags += [ "-fomit-frame-pointer" ] + if (arm_version >= 7 && (arm_use_neon || arm_optionally_use_neon)) { + sources = gypi_skia_opts.armv7_sources + if (arm_use_neon || arm_optionally_use_neon) { + sources += gypi_skia_opts.neon_sources + + # Root build config sets -mfpu=$arm_fpu, which we expect to be neon + # when running this. + if (!arm_use_neon) { + configs -= [ "//build/config/compiler:compiler_arm_fpu" ] + cflags += [ "-mfpu=neon" ] + } + } + } else { + sources = gypi_skia_opts.none_sources + } + } else if (current_cpu == "mipsel") { + cflags += [ "-fomit-frame-pointer" ] + + if (mips_dsp_rev >= 1) { + sources = gypi_skia_opts.mips_dsp_sources + if (mips_dsp_rev >= 2) { + sources += [ + # Chrome-specific. + "ext/convolver_mips_dspr2.cc", + "ext/convolver_mips_dspr2.h", + ] + } + } else { + sources = gypi_skia_opts.none_sources + } + } else { + assert(false, "Need to port cpu specific stuff from skia_library_opts.gyp") + } + + if (is_android && !is_debug) { + configs -= [ "//build/config/compiler:optimize" ] + configs += [ "//build/config/compiler:optimize_max" ] + } + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ + ":skia_config", + ":skia_library_config", + "//build/config/compiler:no_chromium_code", + ] + + deps = [ + "//base", + ] + + visibility = [ ":skia" ] +} + +test("skia_unittests") { + sources = [ + "ext/analysis_canvas_unittest.cc", + "ext/bitmap_platform_device_mac_unittest.cc", + "ext/convolver_unittest.cc", + "ext/image_operations_unittest.cc", + "ext/pixel_ref_utils_unittest.cc", + "ext/platform_canvas_unittest.cc", + "ext/recursive_gaussian_convolution_unittest.cc", + "ext/refptr_unittest.cc", + "ext/skia_utils_ios_unittest.mm", + "ext/skia_utils_mac_unittest.mm", + ] + + if (!is_win && !is_mac) { + sources -= [ "ext/platform_canvas_unittest.cc" ] + } + + deps = [ + ":skia", + "//base", + "//base/test:run_all_unittests", + "//cc:test_support", # TODO: Fix this test to not depend on cc. + "//testing/gtest", + "//ui/gfx", + "//ui/gfx/geometry", + ] +} + +if (is_linux) { + # TODO(GYP): Figure out which of these work and are needed on other platforms. + executable("image_operations_bench") { + sources = [ + "ext/image_operations_bench.cc", + ] + + deps = [ + ":skia", + "//base", + ] + } + + executable("filter_fuzz_stub") { + sources = [ + "tools/filter_fuzz_stub/filter_fuzz_stub.cc", + ] + + deps = [ + ":skia", + "//base", + ] + } +} diff --git a/engine/src/flutter/skia/OWNERS b/engine/src/flutter/skia/OWNERS new file mode 100644 index 0000000000..6f1af588b9 --- /dev/null +++ b/engine/src/flutter/skia/OWNERS @@ -0,0 +1,20 @@ +alokp@chromium.org +borenet@google.com +bsalomon@google.com +bungeman@google.com +djsollen@google.com +edisonn@google.com +epoger@google.com +fmalita@chromium.org +junov@chromium.org +jvanverth@google.com +mtklein@chromium.org +reed@google.com +rmistry@google.com +robertphillips@google.com +scroggo@google.com +senorblanco@chromium.org +sugoi@google.com +thakis@chromium.org +tomhudson@google.com +vandebo@chromium.org diff --git a/engine/src/flutter/skia/config/SkUserConfig.h b/engine/src/flutter/skia/config/SkUserConfig.h new file mode 100644 index 0000000000..e9990e6ad9 --- /dev/null +++ b/engine/src/flutter/skia/config/SkUserConfig.h @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkUserConfig_DEFINED +#define SkUserConfig_DEFINED + +/* SkTypes.h, the root of the public header files, does the following trick: + + #include + #include + #include + + SkPreConfig.h runs first, and it is responsible for initializing certain + skia defines. + + SkPostConfig.h runs last, and its job is to just check that the final + defines are consistent (i.e. that we don't have mutually conflicting + defines). + + SkUserConfig.h (this file) runs in the middle. It gets to change or augment + the list of flags initially set in preconfig, and then postconfig checks + that everything still makes sense. + + Below are optional defines that add, subtract, or change default behavior + in Skia. Your port can locally edit this file to enable/disable flags as + you choose, or these can be delared on your command line (i.e. -Dfoo). + + By default, this include file will always default to having all of the flags + commented out, so including it will have no effect. +*/ + +/////////////////////////////////////////////////////////////////////////////// + +/* Scalars (the fractional value type in skia) can be implemented either as + floats or 16.16 integers (fixed). Exactly one of these two symbols must be + defined. +*/ +//#define SK_SCALAR_IS_FLOAT +//#define SK_SCALAR_IS_FIXED + + +/* Somewhat independent of how SkScalar is implemented, Skia also wants to know + if it can use floats at all. Naturally, if SK_SCALAR_IS_FLOAT is defined, + then so muse SK_CAN_USE_FLOAT, but if scalars are fixed, SK_CAN_USE_FLOAT + can go either way. + */ +//#define SK_CAN_USE_FLOAT + +/* For some performance-critical scalar operations, skia will optionally work + around the standard float operators if it knows that the CPU does not have + native support for floats. If your environment uses software floating point, + define this flag. + */ +//#define SK_SOFTWARE_FLOAT + + +/* Skia has lots of debug-only code. Often this is just null checks or other + parameter checking, but sometimes it can be quite intrusive (e.g. check that + each 32bit pixel is in premultiplied form). This code can be very useful + during development, but will slow things down in a shipping product. + + By default, these mutually exclusive flags are defined in SkPreConfig.h, + based on the presence or absence of NDEBUG, but that decision can be changed + here. + */ +//#define SK_DEBUG +//#define SK_RELEASE + +#ifdef DCHECK_ALWAYS_ON + #undef SK_RELEASE + #define SK_DEBUG +#endif + +/* If, in debugging mode, Skia needs to stop (presumably to invoke a debugger) + it will call SK_CRASH(). If this is not defined it, it is defined in + SkPostConfig.h to write to an illegal address + */ +//#define SK_CRASH() *(int *)(uintptr_t)0 = 0 + + +/* preconfig will have attempted to determine the endianness of the system, + but you can change these mutually exclusive flags here. + */ +//#define SK_CPU_BENDIAN +//#define SK_CPU_LENDIAN + + +/* Some compilers don't support long long for 64bit integers. If yours does + not, define this to the appropriate type. + */ +//#define SkLONGLONG int64_t + + +/* Some envorinments do not suport writable globals (eek!). If yours does not, + define this flag. + */ +//#define SK_USE_RUNTIME_GLOBALS + +/* If zlib is available and you want to support the flate compression + algorithm (used in PDF generation), define SK_ZLIB_INCLUDE to be the + include path. + */ +//#define SK_ZLIB_INCLUDE +#define SK_ZLIB_INCLUDE "third_party/zlib/zlib.h" + +/* Define this to allow PDF scalars above 32k. The PDF/A spec doesn't allow + them, but modern PDF interpreters should handle them just fine. + */ +//#define SK_ALLOW_LARGE_PDF_SCALARS + +/* Define this to provide font subsetter for font subsetting when generating + PDF documents. + */ +#define SK_SFNTLY_SUBSETTER \ + "third_party/sfntly/cpp/src/sample/chromium/font_subsetter.h" + +/* To write debug messages to a console, skia will call SkDebugf(...) following + printf conventions (e.g. const char* format, ...). If you want to redirect + this to something other than printf, define yours here + */ +//#define SkDebugf(...) MyFunction(__VA_ARGS__) + + +/* If SK_DEBUG is defined, then you can optionally define SK_SUPPORT_UNITTEST + which will run additional self-tests at startup. These can take a long time, + so this flag is optional. + */ +#ifdef SK_DEBUG +#define SK_SUPPORT_UNITTEST +#endif + +/* If cross process SkPictureImageFilters are not explicitly enabled then + they are always disabled. + */ +#ifndef SK_ALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS + #ifndef SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS + #define SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS + #endif +#endif + + +/* If your system embeds skia and has complex event logging, define this + symbol to name a file that maps the following macros to your system's + equivalents: + SK_TRACE_EVENT0(event) + SK_TRACE_EVENT1(event, name1, value1) + SK_TRACE_EVENT2(event, name1, value1, name2, value2) + src/utils/SkDebugTrace.h has a trivial implementation that writes to + the debug output stream. If SK_USER_TRACE_INCLUDE_FILE is not defined, + SkTrace.h will define the above three macros to do nothing. +*/ +#undef SK_USER_TRACE_INCLUDE_FILE + +// ===== Begin Chrome-specific definitions ===== + +#ifdef SK_DEBUG +#define SK_REF_CNT_MIXIN_INCLUDE "sk_ref_cnt_ext_debug.h" +#else +#define SK_REF_CNT_MIXIN_INCLUDE "sk_ref_cnt_ext_release.h" +#endif + +#define SK_SCALAR_IS_FLOAT +#undef SK_SCALAR_IS_FIXED + +#define SK_MSCALAR_IS_FLOAT +#undef SK_MSCALAR_IS_DOUBLE + +#define GR_MAX_OFFSCREEN_AA_DIM 512 + +// Log the file and line number for assertions. +#define SkDebugf(...) SkDebugf_FileLine(__FILE__, __LINE__, false, __VA_ARGS__) +SK_API void SkDebugf_FileLine(const char* file, int line, bool fatal, + const char* format, ...); + +// Marking the debug print as "fatal" will cause a debug break, so we don't need +// a separate crash call here. +#define SK_DEBUGBREAK(cond) do { if (!(cond)) { \ + SkDebugf_FileLine(__FILE__, __LINE__, true, \ + "%s:%d: failed assertion \"%s\"\n", \ + __FILE__, __LINE__, #cond); } } while (false) + +#if !defined(ANDROID) // On Android, we use the skia default settings. +#define SK_A32_SHIFT 24 +#define SK_R32_SHIFT 16 +#define SK_G32_SHIFT 8 +#define SK_B32_SHIFT 0 +#endif + +#if defined(SK_BUILD_FOR_WIN32) + +#define SK_BUILD_FOR_WIN + +// Skia uses this deprecated bzero function to fill zeros into a string. +#define bzero(str, len) memset(str, 0, len) + +#elif defined(SK_BUILD_FOR_MAC) + +#define SK_CPU_LENDIAN +#undef SK_CPU_BENDIAN + +#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) + +// Prefer FreeType's emboldening algorithm to Skia's +// TODO: skia used to just use hairline, but has improved since then, so +// we should revisit this choice... +#define SK_USE_FREETYPE_EMBOLDEN + +#if defined(SK_BUILD_FOR_UNIX) && defined(SK_CPU_BENDIAN) +// Above we set the order for ARGB channels in registers. I suspect that, on +// big endian machines, you can keep this the same and everything will work. +// The in-memory order will be different, of course, but as long as everything +// is reading memory as words rather than bytes, it will all work. However, if +// you find that colours are messed up I thought that I would leave a helpful +// locator for you. Also see the comments in +// base/gfx/bitmap_platform_device_linux.h +#error Read the comment at this location +#endif + +#endif + +// The default crash macro writes to badbeef which can cause some strange +// problems. Instead, pipe this through to the logging function as a fatal +// assertion. +#define SK_CRASH() SkDebugf_FileLine(__FILE__, __LINE__, true, "SK_CRASH") + +// These flags are no longer defined in Skia, but we have them (temporarily) +// until we update our call-sites (typically these are for API changes). +// +// Remove these as we update our sites. +// +#ifndef SK_SUPPORT_LEGACY_GETTOPDEVICE +# define SK_SUPPORT_LEGACY_GETTOPDEVICE +#endif + +#ifndef SK_LEGACY_DRAWPICTURECALLBACK +# define SK_LEGACY_DRAWPICTURECALLBACK +#endif + +#ifndef SK_SUPPORT_LEGACY_GETDEVICE +# define SK_SUPPORT_LEGACY_GETDEVICE +#endif + +#ifndef SK_SUPPORT_LEGACY_PUBLIC_IMAGEINFO_FIELDS +# define SK_SUPPORT_LEGACY_PUBLIC_IMAGEINFO_FIELDS +#endif + +#ifndef SK_IGNORE_ETC1_SUPPORT +# define SK_IGNORE_ETC1_SUPPORT +#endif + +#ifndef SK_SUPPORT_LEGACY_BOOL_ONGETINFO +# define SK_SUPPORT_LEGACY_BOOL_ONGETINFO +#endif + +#ifndef SK_IGNORE_GPU_DITHER +# define SK_IGNORE_GPU_DITHER +#endif + +#ifndef SK_SUPPORT_LEGACY_INT_COLORMATRIX +# define SK_SUPPORT_LEGACY_INT_COLORMATRIX +#endif + +#ifndef SK_LEGACY_STROKE_CURVES +# define SK_LEGACY_STROKE_CURVES +#endif + +///////////////////////// Imported from BUILD.gn and skia_common.gypi + +/* In some places Skia can use static initializers for global initialization, + * or fall back to lazy runtime initialization. Chrome always wants the latter. + */ +#define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0 + +/* Forcing the unoptimized path for the offset image filter in skia until + * all filters used in Blink support the optimized path properly + */ +#define SK_DISABLE_OFFSETIMAGEFILTER_OPTIMIZATION + +/* This flag forces Skia not to use typographic metrics with GDI. + */ +#define SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS + +#define IGNORE_ROT_AA_RECT_OPT +#define SK_IGNORE_BLURRED_RRECT_OPT +#define SK_USE_DISCARDABLE_SCALEDIMAGECACHE +#define SK_WILL_NEVER_DRAW_PERSPECTIVE_TEXT + +#define SK_ATTR_DEPRECATED SK_NOTHING_ARG1 +#define SK_ENABLE_INST_COUNT 0 +#define GR_GL_CUSTOM_SETUP_HEADER "GrGLConfig_chrome.h" + +// ===== End Chrome-specific definitions ===== + +#endif diff --git a/engine/src/flutter/skia/config/sk_ref_cnt_ext_debug.h b/engine/src/flutter/skia/config/sk_ref_cnt_ext_debug.h new file mode 100644 index 0000000000..b2b66adba7 --- /dev/null +++ b/engine/src/flutter/skia/config/sk_ref_cnt_ext_debug.h @@ -0,0 +1,48 @@ +// Copyright 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. + +#ifndef SK_REF_CNT_EXT_DEBUG_H_ +#define SK_REF_CNT_EXT_DEBUG_H_ + +#ifdef SK_REF_CNT_EXT_RELEASE_H_ +#error Only one SkRefCnt should be used. +#endif + +// Alternate implementation of SkRefCnt for Chromium debug builds +class SK_API SkRefCnt : public SkRefCntBase { +public: + SkRefCnt() : flags_(0) {} + void ref() const { SkASSERT(flags_ != AdoptionRequired_Flag); SkRefCntBase::ref(); } + void adopted() const { flags_ |= Adopted_Flag; } + void requireAdoption() const { flags_ |= AdoptionRequired_Flag; } + void deref() const { SkRefCntBase::unref(); } +private: + enum { + Adopted_Flag = 0x1, + AdoptionRequired_Flag = 0x2, + }; + + mutable int flags_; +}; + +// Bootstrap for Blink's WTF::RefPtr + +namespace WTF { + inline void adopted(const SkRefCnt* object) { + if (!object) + return; + object->adopted(); + } + inline void requireAdoption(const SkRefCnt* object) { + if (!object) + return; + object->requireAdoption(); + } +}; + +using WTF::adopted; +using WTF::requireAdoption; + +#endif + diff --git a/engine/src/flutter/skia/config/sk_ref_cnt_ext_release.h b/engine/src/flutter/skia/config/sk_ref_cnt_ext_release.h new file mode 100644 index 0000000000..7736c84cf7 --- /dev/null +++ b/engine/src/flutter/skia/config/sk_ref_cnt_ext_release.h @@ -0,0 +1,19 @@ +// Copyright 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. + +#ifndef SK_REF_CNT_EXT_RELEASE_H_ +#define SK_REF_CNT_EXT_RELEASE_H_ + +#ifdef SK_REF_CNT_EXT_DEBUG_H_ +#error Only one SkRefCnt should be used. +#endif + +// Alternate implementation of SkRefCnt for Chromium release builds +class SK_API SkRefCnt : public SkRefCntBase { +public: + void deref() const { SkRefCntBase::unref(); } +}; + +#endif + diff --git a/engine/src/flutter/skia/skia.gyp b/engine/src/flutter/skia/skia.gyp new file mode 100644 index 0000000000..109adffdbe --- /dev/null +++ b/engine/src/flutter/skia/skia.gyp @@ -0,0 +1,150 @@ +# Copyright (c) 2012 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. + +{ + 'conditions': [ + # In component mode (shared_lib), we build all of skia as a single DLL. + # However, in the static mode, we need to build skia as multiple targets + # in order to support the use case where a platform (e.g. Android) may + # already have a copy of skia as a system library. + ['component=="static_library"', { + 'targets': [ + { + 'target_name': 'skia_library', + 'type': 'static_library', + 'includes': [ + 'skia_library.gypi', + 'skia_common.gypi', + '../build/android/increase_size_for_speed.gypi', + # Disable LTO due to compiler error + # in mems_in_disjoint_alias_sets_p, at alias.c:393 + # crbug.com/422255 + '../build/android/disable_lto.gypi', + ], + }, + ], + }], + ['component=="static_library"', { + 'targets': [ + { + 'target_name': 'skia', + 'type': 'none', + 'dependencies': [ + 'skia_library', + 'skia_chrome', + ], + 'export_dependent_settings': [ + 'skia_library', + 'skia_chrome', + ], + }, + { + 'target_name': 'skia_chrome', + 'type': 'static_library', + 'includes': [ + 'skia_chrome.gypi', + 'skia_common.gypi', + '../build/android/increase_size_for_speed.gypi', + ], + }, + ], + }, + { # component != static_library + 'targets': [ + { + 'target_name': 'skia', + 'type': 'shared_library', + 'includes': [ + 'skia_library.gypi', + 'skia_chrome.gypi', + 'skia_common.gypi', + '../build/android/increase_size_for_speed.gypi', + ], + 'defines': [ + 'SKIA_DLL', + 'SKIA_IMPLEMENTATION=1', + 'GR_GL_IGNORE_ES3_MSAA=0', + ], + 'direct_dependent_settings': { + 'defines': [ + 'SKIA_DLL', + 'GR_GL_IGNORE_ES3_MSAA=0', + ], + }, + }, + { + 'target_name': 'skia_library', + 'type': 'none', + }, + { + 'target_name': 'skia_chrome', + 'type': 'none', + }, + ], + }], + ], + + # targets that are not dependent upon the component type + 'targets': [ + { + 'target_name': 'skia_chrome_opts', + 'type': 'static_library', + 'include_dirs': [ + '..', + 'config', + '../third_party/skia/include/core', + ], + 'conditions': [ + [ 'os_posix == 1 and OS != "mac" and OS != "android" and \ + target_arch != "arm" and target_arch != "mipsel" and \ + target_arch != "arm64" and target_arch != "mips64el"', { + 'cflags': [ + '-msse2', + ], + }], + [ 'target_arch != "arm" and target_arch != "mipsel" and \ + target_arch != "arm64" and target_arch != "mips64el"', { + 'sources': [ + 'ext/convolver_SSE2.cc', + 'ext/convolver_SSE2.h', + ], + }], + [ 'target_arch == "mipsel" and mips_dsp_rev >= 2',{ + 'sources': [ + 'ext/convolver_mips_dspr2.cc', + 'ext/convolver_mips_dspr2.h', + ], + }], + ], + }, + { + 'target_name': 'image_operations_bench', + 'type': 'executable', + 'dependencies': [ + '../base/base.gyp:base', + 'skia', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'ext/image_operations_bench.cc', + ], + }, + { + 'target_name': 'filter_fuzz_stub', + 'type': 'executable', + 'dependencies': [ + '../base/base.gyp:base', + 'skia.gyp:skia', + ], + 'sources': [ + 'tools/filter_fuzz_stub/filter_fuzz_stub.cc', + ], + 'includes': [ + '../build/android/increase_size_for_speed.gypi', + ], + }, + ], +} diff --git a/engine/src/flutter/skia/skia_Prefix.pch b/engine/src/flutter/skia/skia_Prefix.pch new file mode 100644 index 0000000000..5541dea063 --- /dev/null +++ b/engine/src/flutter/skia/skia_Prefix.pch @@ -0,0 +1,12 @@ +// Copyright (c) 2008 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. + +// Prefix header for all source files in the 'Skia' framework. + +#ifdef __OBJC__ +#import +#endif + +// Include the Skia prefix file. +#include "precompiled.cc" diff --git a/engine/src/flutter/skia/skia_library_opts.gyp b/engine/src/flutter/skia/skia_library_opts.gyp new file mode 100644 index 0000000000..58f036f016 --- /dev/null +++ b/engine/src/flutter/skia/skia_library_opts.gyp @@ -0,0 +1,229 @@ +# Copyright 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. + +# This gyp file contains the platform-specific optimizations for Skia +{ + 'variables': { + 'skia_src_path': '../third_party/skia/src', + 'includes': [ '../third_party/skia/gyp/opts.gypi' ], + 'include_dirs': [ + '../third_party/skia/include/core', + '../third_party/skia/include/effects', + '../third_party/skia/include/utils', + '../third_party/skia/src/core', + '../third_party/skia/src/opts', + '../third_party/skia/src/utils', + ], + }, + + 'targets': [ + # Due to an unfortunate intersection of lameness between gcc and gyp, + # we have to build the *_SSE2.cpp files in a separate target. The + # gcc lameness is that, in order to compile SSE2 intrinsics code, it + # must be passed the -msse2 flag. However, with this flag, it may + # emit SSE2 instructions even for scalar code, such as the CPUID + # test used to test for the presence of SSE2. So that, and all other + # code must be compiled *without* -msse2. The gyp lameness is that it + # does not allow file-specific CFLAGS, so we must create this extra + # target for those files to be compiled with -msse2. + # + # This is actually only a problem on 32-bit Linux (all Intel Macs have + # SSE2, Linux x86_64 has SSE2 by definition, and MSC will happily emit + # SSE2 from instrinsics, which generating plain ol' 386 for everything + # else). However, to keep the .gyp file simple and avoid platform-specific + # build breakage, we do this on all platforms. + + # For about the same reason, we need to compile the ARM opts files + # separately as well. + { + 'target_name': 'skia_opts', + 'type': 'static_library', + 'includes': [ + 'skia_common.gypi', + '../build/android/increase_size_for_speed.gypi', + # Disable LTO due to compiler error + # in mems_in_disjoint_alias_sets_p, at alias.c:393 + # crbug.com/422255 + '../build/android/disable_lto.gypi', + ], + 'include_dirs': [ '<@(include_dirs)' ], + 'conditions': [ + [ 'os_posix == 1 and OS != "mac" and OS != "android" and \ + target_arch != "arm" and target_arch != "arm64" and \ + target_arch != "mipsel" and target_arch != "mips64el"', { + 'cflags': [ '-msse2' ], + }], + [ 'target_arch != "arm" and target_arch != "mipsel" and \ + target_arch != "arm64" and target_arch != "mips64el"', { + 'sources': [ '<@(sse2_sources)' ], + 'dependencies': [ + 'skia_opts_ssse3', + 'skia_opts_sse41', + ], + }], + [ 'target_arch == "arm"', { + 'conditions': [ + [ 'arm_version >= 7', { + 'sources': [ '<@(armv7_sources)' ], + }, { # arm_version < 7 + 'sources': [ '<@(none_sources)' ], + }], + [ 'arm_version >= 7 and (arm_neon == 1 or arm_neon_optional == 1)', { + 'dependencies': [ + 'skia_opts_neon', + ] + }], + ], + # The assembly uses the frame pointer register (r7 in Thumb/r11 in + # ARM), the compiler doesn't like that. Explicitly remove the + # -fno-omit-frame-pointer flag for Android, as that gets added to all + # targets via common.gypi. + 'cflags!': [ + '-fno-omit-frame-pointer', + '-marm', + '-mapcs-frame', + ], + 'cflags': [ + '-fomit-frame-pointer', + ], + }], + [ 'target_arch == "mipsel"',{ + 'cflags': [ '-fomit-frame-pointer' ], + 'conditions': [ + [ 'mips_dsp_rev >= 1', { + 'sources': [ '<@(mips_dsp_sources)' ], + }, { # mips_dsp_rev == 0 + 'sources': [ '<@(none_sources)' ], + }], + ], + }], + [ 'target_arch == "mips64el"',{ + 'cflags': [ '-fomit-frame-pointer' ], + 'sources': [ '<@(none_sources)' ], + }], + [ 'target_arch == "arm64"', { + 'sources': [ '<@(arm64_sources)' ], + }], + ], + }, + # For the same lame reasons as what is done for skia_opts, we have to + # create another target specifically for SSSE3 code as we would not want + # to compile the SSE2 code with -mssse3 which would potentially allow + # gcc to generate SSSE3 code. + { + 'target_name': 'skia_opts_ssse3', + 'type': 'static_library', + 'includes': [ + 'skia_common.gypi', + '../build/android/increase_size_for_speed.gypi', + ], + 'include_dirs': [ '<@(include_dirs)' ], + 'conditions': [ + [ 'OS in ["linux", "freebsd", "openbsd", "solaris", "android"]', { + 'cflags': [ '-mssse3' ], + }], + [ 'OS == "mac"', { + 'xcode_settings': { + 'GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS': 'YES', + }, + }], + [ 'OS == "win" and clang == 1', { + # cl.exe's /arch flag doesn't have a setting for SSSE3, and cl.exe + # doesn't need it for intrinsics. clang-cl does need it, though. + 'msvs_settings': { + 'VCCLCompilerTool': { 'AdditionalOptions': [ '-mssse3' ] }, + }, + }], + [ 'OS == "win"', { + 'defines' : [ 'SK_CPU_SSE_LEVEL=31' ], + }], + [ 'target_arch != "arm" and target_arch != "arm64" and \ + target_arch != "mipsel" and target_arch != "mips64el"', { + 'sources': [ '<@(ssse3_sources)' ], + }], + ], + }, + # For the same lame reasons as what is done for skia_opts, we also have to + # create another target specifically for SSE4.1 code as we would not want + # to compile the SSE2 code with -msse4.1 which would potentially allow + # gcc to generate SSE4.1 code. + { + 'target_name': 'skia_opts_sse41', + 'type': 'static_library', + 'includes': [ + 'skia_common.gypi', + '../build/android/increase_size_for_speed.gypi', + ], + 'include_dirs': [ '<@(include_dirs)' ], + 'sources': [ '<@(sse41_sources)' ], + 'conditions': [ + [ 'OS in ["linux", "freebsd", "openbsd", "solaris", "android"]', { + 'cflags': [ '-msse4.1' ], + }], + [ 'OS == "mac"', { + 'xcode_settings': { + 'GCC_ENABLE_SSE41_EXTENSIONS': 'YES', + }, + }], + [ 'OS == "win" and clang == 1', { + # cl.exe's /arch flag doesn't have a setting for SSE4.1, and cl.exe + # doesn't need it for intrinsics. clang-cl does need it, though. + 'msvs_settings': { + 'VCCLCompilerTool': { 'AdditionalOptions': [ '-msse4.1' ] }, + }, + }], + [ 'OS == "win"', { + 'defines' : [ 'SK_CPU_SSE_LEVEL=41' ], + }], + ], + }, + { + 'target_name': 'skia_opts_none', + 'type': 'static_library', + 'includes': [ + 'skia_common.gypi', + '../build/android/increase_size_for_speed.gypi', + ], + 'include_dirs': [ '<@(include_dirs)' ], + 'sources': [ '<@(none_sources)' ], + }, + ], + 'conditions': [ + # NEON code must be compiled with -mfpu=neon which also affects scalar + # code. To support dynamic NEON code paths, we need to build all + # NEON-specific sources in a separate static library. The situation + # is very similar to the SSSE3 one. + ['target_arch == "arm" and (arm_neon == 1 or arm_neon_optional == 1)', { + 'targets': [ + { + 'target_name': 'skia_opts_neon', + 'type': 'static_library', + 'includes': [ + 'skia_common.gypi', + '../build/android/increase_size_for_speed.gypi', + # Disable LTO due to Neon issues + # crbug.com/408997 + '../build/android/disable_lto.gypi', + ], + 'include_dirs': [ '<@(include_dirs)' ], + 'cflags!': [ + '-fno-omit-frame-pointer', + '-mfpu=vfp', # remove them all, just in case. + '-mfpu=vfpv3', + '-mfpu=vfpv3-d16', + ], + 'cflags': [ + '-mfpu=neon', + '-fomit-frame-pointer', + ], + 'ldflags': [ + '-march=armv7-a', + '-Wl,--fix-cortex-a8', + ], + 'sources': [ '<@(neon_sources)' ], + }, + ], + }], + ], +} diff --git a/engine/src/flutter/skia/skia_test_expectations.txt b/engine/src/flutter/skia/skia_test_expectations.txt new file mode 100644 index 0000000000..4327f6e172 --- /dev/null +++ b/engine/src/flutter/skia/skia_test_expectations.txt @@ -0,0 +1,51 @@ +# TEMPORARY overrides of +# src/third_party/WebKit/LayoutTests/platform/chromium/test_expectations.txt +# that are associated with changes to the Skia code. +# +# GUIDELINES: +# - This file should be empty most of the time. +# - Expectations should only be added TEMPORARILY, as a step towards +# rebaselining layout test results. If any one expectation remains in here +# for more than a week or two, then we are probably doing something wrong. +# - Expectations from this file should NOT be rolled into any other +# test_expectations file. If there is a test that we expect to fail +# indefinitely, then we should add that test to the roach motel that is +# src/third_party/WebKit/LayoutTests/platform/chromium/test_expectations.txt +# - Tests listed in this file should NOT be rebaselined by WebKit Gardeners, +# unless they have made arrangements with Skia developers. +# +# For more information, see https://bugs.webkit.org/show_bug.cgi?id=86749 +# or email skia-dev@google.com . +# +# INSTRUCTIONS: +# If you are rolling Skia's DEPS within Chrome, and trybot results indicate +# that the DEPS roll would break some webkit layout_tests, please follow +# these steps: +# +# 1. Confirm that those layout_test failures are "reasonable"-- Are they +# actually improvements, not regressions? Or maybe they are very minor +# differences that go along with a performance improvement? +# If not, please fix Skia rather than rolling in the version that will +# regress the webkit layout_tests. +# +# 2. File a bug to yourself to track the rebaselining of results caused by +# your Skia DEPS roll. +# +# 3. Add one or more lines to this file, in the same syntax used in the main +# test_expectations file, to mark those tests as expected-to-fail. +# Add this file to your DEPS roll CL. +# +# 4. Run your DEPS roll CL through the trybots again, and confirm your CL does +# not cause any layout tests to fail. (If there are still failures as a +# result of your CL, you probably didn't add the test expectations correctly.) +# +# 5. Commit your DEPS roll CL, and keep an eye on the waterfall bots to make +# sure nothing goes red. +# +# 6. Make sure to rebaseline the layout tests as soon as possible! The longer +# we leave overrides in this file, the harder it will be to rebaseline those +# tests (because other rendering changes might creep in). +# +# START OVERRIDES HERE + +# END OVERRIDES HERE (this line ensures that the file is newline-terminated) diff --git a/engine/src/flutter/skia/skia_tests.gyp b/engine/src/flutter/skia/skia_tests.gyp new file mode 100644 index 0000000000..698493d306 --- /dev/null +++ b/engine/src/flutter/skia/skia_tests.gyp @@ -0,0 +1,64 @@ +# Copyright 2014 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'targets': [ + { + 'target_name': 'skia_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + '../base/base.gyp:base', + '../base/base.gyp:run_all_unittests', + '../testing/gtest.gyp:gtest', + '../skia/skia.gyp:skia', + '../ui/gfx/gfx.gyp:gfx', + '../ui/gfx/gfx.gyp:gfx_geometry', + ], + 'sources': [ + 'ext/analysis_canvas_unittest.cc', + 'ext/bitmap_platform_device_mac_unittest.cc', + 'ext/convolver_unittest.cc', + 'ext/image_operations_unittest.cc', + 'ext/pixel_ref_utils_unittest.cc', + 'ext/platform_canvas_unittest.cc', + 'ext/recursive_gaussian_convolution_unittest.cc', + 'ext/refptr_unittest.cc', + 'ext/skia_utils_ios_unittest.mm', + 'ext/skia_utils_mac_unittest.mm', + ], + 'conditions': [ + ['OS != "win" and OS != "mac"', { + 'sources!': [ + 'ext/platform_canvas_unittest.cc', + ], + }], + ['OS == "android"', { + 'dependencies': [ + '../testing/android/native_test.gyp:native_test_native_code', + ], + }], + ], + }, + ], + 'conditions': [ + ['OS == "android"', { + 'targets': [ + { + 'target_name': 'skia_unittests_apk', + 'type': 'none', + 'dependencies': [ + 'skia_unittests', + ], + 'variables': { + 'test_suite_name': 'skia_unittests', + }, + 'includes': [ '../build/apk_test.gypi' ], + }, + ], + }], + ], +} diff --git a/engine/src/flutter/skia/tools/filter_fuzz_stub/filter_fuzz_stub.cc b/engine/src/flutter/skia/tools/filter_fuzz_stub/filter_fuzz_stub.cc new file mode 100644 index 0000000000..4d5ec61ae1 --- /dev/null +++ b/engine/src/flutter/skia/tools/filter_fuzz_stub/filter_fuzz_stub.cc @@ -0,0 +1,90 @@ +// Copyright 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. + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkFlattenableSerialization.h" +#include "third_party/skia/include/core/SkImageFilter.h" + +namespace { + +static const int BitmapSize = 24; + +bool ReadTestCase(const char* filename, std::string* ipc_filter_message) { + base::FilePath filepath = base::FilePath::FromUTF8Unsafe(filename); + + if (!base::ReadFileToString(filepath, ipc_filter_message)) { + LOG(ERROR) << filename << ": couldn't read file."; + return false; + } + + return true; +} + +void RunTestCase(std::string& ipc_filter_message, SkBitmap& bitmap, + SkCanvas* canvas) { + // This call shouldn't crash or cause ASAN to flag any memory issues + // If nothing bad happens within this call, everything is fine + SkFlattenable* flattenable = SkValidatingDeserializeFlattenable( + ipc_filter_message.c_str(), ipc_filter_message.size(), + SkImageFilter::GetFlattenableType()); + + // Adding some info, but the test passed if we got here without any trouble + if (flattenable != NULL) { + LOG(INFO) << "Valid stream detected."; + // Let's see if using the filters can cause any trouble... + SkPaint paint; + paint.setImageFilter(static_cast(flattenable))->unref(); + canvas->save(); + canvas->clipRect(SkRect::MakeXYWH( + 0, 0, SkIntToScalar(BitmapSize), SkIntToScalar(BitmapSize))); + + // This call shouldn't crash or cause ASAN to flag any memory issues + // If nothing bad happens within this call, everything is fine + canvas->drawBitmap(bitmap, 0, 0, &paint); + + LOG(INFO) << "Filter DAG rendered successfully"; + canvas->restore(); + } else { + LOG(INFO) << "Invalid stream detected."; + } +} + +bool ReadAndRunTestCase(const char* filename, SkBitmap& bitmap, + SkCanvas* canvas) { + std::string ipc_filter_message; + + LOG(INFO) << "Test case: " << filename; + + // ReadTestCase will print a useful error message if it fails. + if (!ReadTestCase(filename, &ipc_filter_message)) + return false; + + RunTestCase(ipc_filter_message, bitmap, canvas); + + return true; +} + +} + +int main(int argc, char** argv) { + int ret = 0; + + SkBitmap bitmap; + bitmap.allocN32Pixels(BitmapSize, BitmapSize); + SkCanvas canvas(bitmap); + canvas.clear(0x00000000); + + for (int i = 1; i < argc; i++) + if (!ReadAndRunTestCase(argv[i], bitmap, &canvas)) + ret = 2; + + // Cluster-Fuzz likes "#EOF" as the last line of output to help distinguish + // successful runs from crashes. + printf("#EOF\n"); + + return ret; +} + diff --git a/engine/src/flutter/testing/BUILD.gn b/engine/src/flutter/testing/BUILD.gn new file mode 100644 index 0000000000..fc50637493 --- /dev/null +++ b/engine/src/flutter/testing/BUILD.gn @@ -0,0 +1,13 @@ +# Copyright 2014 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. + +source_set("gmock_mutant") { + sources = [ + "gmock_mutant.h", # gMock helpers + ] + + deps = [ + "//base", + ] +} diff --git a/engine/src/flutter/testing/OWNERS b/engine/src/flutter/testing/OWNERS new file mode 100644 index 0000000000..72e8ffc0db --- /dev/null +++ b/engine/src/flutter/testing/OWNERS @@ -0,0 +1 @@ +* diff --git a/engine/src/flutter/testing/PRESUBMIT.py b/engine/src/flutter/testing/PRESUBMIT.py new file mode 100644 index 0000000000..5e8fc90625 --- /dev/null +++ b/engine/src/flutter/testing/PRESUBMIT.py @@ -0,0 +1,25 @@ +# Copyright (c) 2012 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. + +"""Top-level presubmit script for testing. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details on the presubmit API built into depot_tools. +""" + + +def CommonChecks(input_api, output_api): + output = [] + blacklist = [r'gmock.*', r'gtest.*'] + output.extend(input_api.canned_checks.RunPylint( + input_api, output_api, black_list=blacklist)) + return output + + +def CheckChangeOnUpload(input_api, output_api): + return CommonChecks(input_api, output_api) + + +def CheckChangeOnCommit(input_api, output_api): + return CommonChecks(input_api, output_api) diff --git a/engine/src/flutter/testing/android/OWNERS b/engine/src/flutter/testing/android/OWNERS new file mode 100644 index 0000000000..0242c2d14c --- /dev/null +++ b/engine/src/flutter/testing/android/OWNERS @@ -0,0 +1,7 @@ +aruslan@chromium.org +dtrainor@chromium.org +miguelg@chromium.org +nyquist@chromium.org +skyostil@chromium.org +tedchoc@chromium.org +yfriedman@chromium.org diff --git a/engine/src/flutter/testing/android/appurify_support.gyp b/engine/src/flutter/testing/android/appurify_support.gyp new file mode 100644 index 0000000000..2904368e72 --- /dev/null +++ b/engine/src/flutter/testing/android/appurify_support.gyp @@ -0,0 +1,22 @@ +# Copyright 2015 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. + +{ + 'conditions': [ + ['OS=="android"', { + 'targets': [ + { + 'target_name': 'appurify_support_java', + 'type': 'none', + 'variables': { + 'java_in_dir': '../../testing/android/appurify_support/java', + }, + 'includes': [ + '../../build/java.gypi', + ], + }, + ], + }], + ], +} diff --git a/engine/src/flutter/testing/android/appurify_support/BUILD.gn b/engine/src/flutter/testing/android/appurify_support/BUILD.gn new file mode 100644 index 0000000000..871f9d0dd8 --- /dev/null +++ b/engine/src/flutter/testing/android/appurify_support/BUILD.gn @@ -0,0 +1,15 @@ +# Copyright 2015 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. + +import("//build/config/android/rules.gni") + +# GYP: //testing/android/appurify_support.gyp:appurify_support_java +android_library("appurify_support_java") { + chromium_code = true + + java_files = [ + "java/src/org/chromium/test/support/ResultsBundleGenerator.java", + "java/src/org/chromium/test/support/RobotiumBundleGenerator.java", + ] +} diff --git a/engine/src/flutter/testing/android/appurify_support/java/src/org/chromium/test/support/ResultsBundleGenerator.java b/engine/src/flutter/testing/android/appurify_support/java/src/org/chromium/test/support/ResultsBundleGenerator.java new file mode 100644 index 0000000000..c9588d90d4 --- /dev/null +++ b/engine/src/flutter/testing/android/appurify_support/java/src/org/chromium/test/support/ResultsBundleGenerator.java @@ -0,0 +1,30 @@ +// Copyright 2015 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. + +package org.chromium.test.support; + +import android.os.Bundle; + +import java.util.Map; + +/** + * Creates a results Bundle. + */ +public interface ResultsBundleGenerator { + + /** Indicates the state of a test. + */ + static enum TestResult { + PASSED, FAILED, ERROR, UNKNOWN + } + + /** Creates a bundle of test results from the provided raw results. + + Note: actual bundle content and format may vary. + + @param rawResults A map between test names and test results. + */ + Bundle generate(Map rawResults); +} + diff --git a/engine/src/flutter/testing/android/appurify_support/java/src/org/chromium/test/support/RobotiumBundleGenerator.java b/engine/src/flutter/testing/android/appurify_support/java/src/org/chromium/test/support/RobotiumBundleGenerator.java new file mode 100644 index 0000000000..167e7b92a4 --- /dev/null +++ b/engine/src/flutter/testing/android/appurify_support/java/src/org/chromium/test/support/RobotiumBundleGenerator.java @@ -0,0 +1,56 @@ +// Copyright 2015 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. + +package org.chromium.test.support; + +import android.app.Instrumentation; +import android.os.Bundle; +import android.util.Log; + +import java.util.Map; + +/** + * Creates a results bundle that emulates the one created by Robotium. + */ +public class RobotiumBundleGenerator implements ResultsBundleGenerator { + + private static final String TAG = "RobotiumBundleGenerator"; + + public Bundle generate(Map rawResults) { + int testsPassed = 0; + int testsFailed = 0; + + for (Map.Entry entry : rawResults.entrySet()) { + switch (entry.getValue()) { + case PASSED: + ++testsPassed; + break; + case FAILED: + // TODO(jbudorick): Remove this log message once AMP execution and + // results handling has been stabilized. + Log.d(TAG, "FAILED: " + entry.getKey()); + ++testsFailed; + break; + default: + Log.w(TAG, "Unhandled: " + entry.getKey() + ", " + + entry.getValue().toString()); + break; + } + } + + StringBuilder resultBuilder = new StringBuilder(); + if (testsFailed > 0) { + resultBuilder.append( + "\nFAILURES!!! Tests run: " + Integer.toString(rawResults.size()) + + ", Failures: " + Integer.toString(testsFailed) + ", Errors: 0"); + } else { + resultBuilder.append("\nOK (" + Integer.toString(testsPassed) + " tests)"); + } + + Bundle resultsBundle = new Bundle(); + resultsBundle.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + resultBuilder.toString()); + return resultsBundle; + } +} diff --git a/engine/src/flutter/testing/android/broker/BUILD.gn b/engine/src/flutter/testing/android/broker/BUILD.gn new file mode 100644 index 0000000000..8daa040832 --- /dev/null +++ b/engine/src/flutter/testing/android/broker/BUILD.gn @@ -0,0 +1,13 @@ +# Copyright 2015 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. + +import("//build/config/android/rules.gni") + +# GYP: //testing/android/on_device_instrumentation.gyp:broker_java +android_library("broker_java") { + chromium_code = true + + java_files = + [ "java/src/org/chromium/test/broker/OnDeviceInstrumentationBroker.java" ] +} diff --git a/engine/src/flutter/testing/android/broker/java/src/org/chromium/test/broker/OnDeviceInstrumentationBroker.java b/engine/src/flutter/testing/android/broker/java/src/org/chromium/test/broker/OnDeviceInstrumentationBroker.java new file mode 100644 index 0000000000..cd755d0897 --- /dev/null +++ b/engine/src/flutter/testing/android/broker/java/src/org/chromium/test/broker/OnDeviceInstrumentationBroker.java @@ -0,0 +1,64 @@ +// Copyright 2015 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. + +package org.chromium.test.broker; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +/** + * An Activity target for OnDeviceInstrumentationDriver that starts the specified + * Instrumentation test. + */ +public class OnDeviceInstrumentationBroker extends Activity { + + public static final String EXTRA_INSTRUMENTATION_PACKAGE = + "org.chromium.test.broker.OnDeviceInstrumentationBroker." + + "InstrumentationPackage"; + public static final String EXTRA_INSTRUMENTATION_CLASS = + "org.chromium.test.broker.OnDeviceInstrumentationBroker." + + "InstrumentationClass"; + public static final String EXTRA_TARGET_ARGS = + "org.chromium.test.broker.OnDeviceInstrumentationBroker.TargetArgs"; + public static final String EXTRA_TEST = + "org.chromium.test.broker.OnDeviceInstrumentationBroker.Test"; + + private static final String TAG = "OnDeviceInstrumentationBroker"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.d(TAG, "onCreate()"); + } + + @Override + public void onStart() { + super.onStart(); + + Intent i = getIntent(); + String instrumentationPackage = i.getStringExtra(EXTRA_INSTRUMENTATION_PACKAGE); + String instrumentationClass = i.getStringExtra(EXTRA_INSTRUMENTATION_CLASS); + Bundle targetArgs = i.getBundleExtra(EXTRA_TARGET_ARGS); + String test = i.getStringExtra(EXTRA_TEST); + + if (instrumentationPackage == null || instrumentationClass == null) { + finish(); + return; + } + + ComponentName instrumentationComponent = + new ComponentName(instrumentationPackage, instrumentationClass); + + if (test != null) { + targetArgs.putString("class", test); + } + + startInstrumentation(instrumentationComponent, null, targetArgs); + finish(); + } +} + diff --git a/engine/src/flutter/testing/android/driver/BUILD.gn b/engine/src/flutter/testing/android/driver/BUILD.gn new file mode 100644 index 0000000000..436ac63c24 --- /dev/null +++ b/engine/src/flutter/testing/android/driver/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2015 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. + +import("//build/config/android/rules.gni") + +# GYP: //testing/android/on_device_instrumentation.gyp:driver_apk +android_apk("driver_apk") { + android_manifest = "java/AndroidManifest.xml" + apk_name = "OnDeviceInstrumentationDriver" + testonly = true + + deps = [ + "//testing/android/appurify_support:appurify_support_java", + "//testing/android/broker:broker_java", + "//testing/android/reporter:reporter_java", + ] + + java_files = + [ "java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java" ] +} diff --git a/engine/src/flutter/testing/android/driver/java/AndroidManifest.xml b/engine/src/flutter/testing/android/driver/java/AndroidManifest.xml new file mode 100644 index 0000000000..c7e99ef106 --- /dev/null +++ b/engine/src/flutter/testing/android/driver/java/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/engine/src/flutter/testing/android/driver/java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java b/engine/src/flutter/testing/android/driver/java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java new file mode 100644 index 0000000000..78c571a4c5 --- /dev/null +++ b/engine/src/flutter/testing/android/driver/java/src/org/chromium/test/driver/OnDeviceInstrumentationDriver.java @@ -0,0 +1,271 @@ +// Copyright 2015 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. + +package org.chromium.test.driver; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.test.InstrumentationTestRunner; +import android.util.Log; + +import org.chromium.test.broker.OnDeviceInstrumentationBroker; +import org.chromium.test.reporter.TestStatusReceiver; +import org.chromium.test.reporter.TestStatusReporter; +import org.chromium.test.support.ResultsBundleGenerator; +import org.chromium.test.support.RobotiumBundleGenerator; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.regex.Pattern; + +/** + * An Instrumentation that drives instrumentation tests from outside the app. + */ +public class OnDeviceInstrumentationDriver extends Instrumentation { + + private static final String TAG = "OnDeviceInstrumentationDriver"; + + private static final String EXTRA_TEST_LIST = + "org.chromium.test.driver.OnDeviceInstrumentationDriver.TestList"; + private static final String EXTRA_TEST_LIST_FILE = + "org.chromium.test.driver.OnDeviceInstrumentationDriver.TestListFile"; + private static final String EXTRA_TARGET_PACKAGE = + "org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetPackage"; + private static final String EXTRA_TARGET_CLASS = + "org.chromium.test.driver.OnDeviceInstrumentationDriver.TargetClass"; + + private static final Pattern COMMA = Pattern.compile(","); + private static final int TEST_WAIT_TIMEOUT = 5 * TestStatusReporter.HEARTBEAT_INTERVAL_MS; + + private boolean mDriverStarted; + private Thread mDriverThread; + private Bundle mTargetArgs; + private String mTargetClass; + private String mTargetPackage; + private List mTestClasses; + + /** Parse any arguments and prepare to run tests. + + @param arguments The arguments to parse. + */ + @Override + public void onCreate(Bundle arguments) { + mTargetArgs = new Bundle(arguments); + mTargetPackage = arguments.getString(EXTRA_TARGET_PACKAGE); + if (mTargetPackage == null) { + fail("No target package."); + return; + } + mTargetArgs.remove(EXTRA_TARGET_PACKAGE); + + mTargetClass = arguments.getString(EXTRA_TARGET_CLASS); + if (mTargetClass == null) { + fail("No target class."); + return; + } + mTargetArgs.remove(EXTRA_TARGET_CLASS); + + mTestClasses = new ArrayList(); + String testList = arguments.getString(EXTRA_TEST_LIST); + if (testList != null) { + mTestClasses.addAll(Arrays.asList(COMMA.split(testList))); + mTargetArgs.remove(EXTRA_TEST_LIST); + } + + String testListFilePath = arguments.getString(EXTRA_TEST_LIST_FILE); + if (testListFilePath != null) { + File testListFile = new File(Environment.getExternalStorageDirectory(), + testListFilePath); + try { + BufferedReader testListFileReader = + new BufferedReader(new FileReader(testListFile)); + String test; + while ((test = testListFileReader.readLine()) != null) { + mTestClasses.add(test); + } + testListFileReader.close(); + } catch (IOException e) { + Log.e(TAG, "Error reading " + testListFile.getAbsolutePath(), e); + } + mTargetArgs.remove(EXTRA_TEST_LIST_FILE); + } + + if (mTestClasses.isEmpty()) { + fail("No tests."); + return; + } + + mDriverThread = new Thread( + new Driver(mTargetPackage, mTargetClass, mTargetArgs, mTestClasses)); + + start(); + } + + /** Start running tests. */ + @Override + public void onStart() { + super.onStart(); + + // Start the driver on its own thread s.t. it can block while the main thread's + // Looper receives and handles messages. + if (!mDriverStarted) { + mDriverThread.start(); + mDriverStarted = true; + } + } + + /** Clean up the reporting service. */ + @Override + public void onDestroy() { + super.onDestroy(); + } + + private class Driver implements Runnable { + + private static final String TAG = OnDeviceInstrumentationDriver.TAG + ".Driver"; + + private Bundle mTargetArgs; + private String mTargetClass; + private String mTargetPackage; + private List mTestClasses; + + public Driver(String targetPackage, String targetClass, Bundle targetArgs, + List testClasses) { + mTargetPackage = targetPackage; + mTargetClass = targetClass; + mTargetArgs = targetArgs; + mTestClasses = testClasses; + } + + private void sendTestStatus(int status, String testClass, String testMethod) { + Bundle statusBundle = new Bundle(); + statusBundle.putString(InstrumentationTestRunner.REPORT_KEY_NAME_CLASS, testClass); + statusBundle.putString(InstrumentationTestRunner.REPORT_KEY_NAME_TEST, testMethod); + sendStatus(status, statusBundle); + } + + /** Run the tests. */ + @Override + public void run() { + final HashMap finished = + new HashMap(); + final Object statusLock = new Object(); + + try { + TestStatusReceiver r = new TestStatusReceiver(); + r.registerCallback(new TestStatusReceiver.StartCallback() { + @Override + public void testStarted(String testClass, String testMethod) { + sendTestStatus(InstrumentationTestRunner.REPORT_VALUE_RESULT_START, + testClass, testMethod); + synchronized (statusLock) { + statusLock.notify(); + } + } + }); + r.registerCallback(new TestStatusReceiver.PassCallback() { + @Override + public void testPassed(String testClass, String testMethod) { + sendTestStatus(InstrumentationTestRunner.REPORT_VALUE_RESULT_OK, testClass, + testMethod); + synchronized (statusLock) { + finished.put(testClass + "#" + testMethod, + ResultsBundleGenerator.TestResult.PASSED); + statusLock.notify(); + } + } + }); + r.registerCallback(new TestStatusReceiver.FailCallback() { + @Override + public void testFailed(String testClass, String testMethod) { + sendTestStatus(InstrumentationTestRunner.REPORT_VALUE_RESULT_ERROR, + testClass, testMethod); + synchronized (statusLock) { + finished.put(testClass + "#" + testMethod, + ResultsBundleGenerator.TestResult.FAILED); + statusLock.notify(); + } + } + }); + r.registerCallback(new TestStatusReceiver.HeartbeatCallback() { + @Override + public void heartbeat() { + Log.i(TAG, "Heartbeat received."); + synchronized (statusLock) { + statusLock.notify(); + } + } + }); + r.register(getContext()); + + for (String t : mTestClasses) { + Intent slaveIntent = new Intent(); + slaveIntent.setComponent(new ComponentName( + mTargetPackage, OnDeviceInstrumentationBroker.class.getName())); + slaveIntent.putExtra( + OnDeviceInstrumentationBroker.EXTRA_INSTRUMENTATION_PACKAGE, + mTargetPackage); + slaveIntent.putExtra( + OnDeviceInstrumentationBroker.EXTRA_INSTRUMENTATION_CLASS, + mTargetClass); + slaveIntent.putExtra(OnDeviceInstrumentationBroker.EXTRA_TEST, t); + slaveIntent.putExtra(OnDeviceInstrumentationBroker.EXTRA_TARGET_ARGS, + mTargetArgs); + slaveIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + getContext().startActivity(slaveIntent); + + synchronized (statusLock) { + while (!finished.containsKey(t)) { + long waitStart = System.currentTimeMillis(); + statusLock.wait(TEST_WAIT_TIMEOUT); + if (System.currentTimeMillis() - waitStart > TEST_WAIT_TIMEOUT) { + Log.e(TAG, t + " has gone missing and is assumed to be dead."); + finished.put(t, ResultsBundleGenerator.TestResult.FAILED); + break; + } + } + } + } + getContext().unregisterReceiver(r); + + } catch (InterruptedException e) { + fail("Interrupted while running tests.", e); + return; + } + pass(new RobotiumBundleGenerator().generate(finished)); + } + + } + + private void fail(String reason) { + Log.e(TAG, reason); + failImpl(reason); + } + + private void fail(String reason, Exception e) { + Log.e(TAG, reason, e); + failImpl(reason); + } + + private void failImpl(String reason) { + Bundle b = new Bundle(); + b.putString("reason", reason); + finish(Activity.RESULT_CANCELED, b); + } + + private void pass(Bundle results) { + finish(Activity.RESULT_OK, results); + } +} diff --git a/engine/src/flutter/testing/android/junit/BUILD.gn b/engine/src/flutter/testing/android/junit/BUILD.gn new file mode 100644 index 0000000000..d479629278 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright 2014 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. + +assert(is_android) + +import("//build/config/android/rules.gni") + +# GYP: //testing/android/junit_test.gyp:junit_test_support +java_library("junit_test_support") { + testonly = true + DEPRECATED_java_in_dir = "java/src" + deps = [ + "//third_party/junit", + "//third_party/mockito:mockito_java", + "//third_party/robolectric:robolectric_java", + "//third_party/robolectric:android-all-4.3_r2-robolectric-0", + ] +} + +# GYP: //testing/android/junit_test.gyp:junit_unit_tests +junit_binary("junit_unittests") { + DEPRECATED_java_in_dir = "javatests/src" +} diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestComputer.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestComputer.java new file mode 100644 index 0000000000..c6782d0d27 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestComputer.java @@ -0,0 +1,76 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.runner.Computer; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +/** + * A Computer that logs the start and end of test cases googletest-style. + */ +public class GtestComputer extends Computer { + + private final GtestLogger mLogger; + + public GtestComputer(GtestLogger logger) { + mLogger = logger; + } + + /** + * A wrapping Runner that logs the start and end of each test case. + */ + private class GtestSuiteRunner extends Runner implements Filterable { + private final Runner mRunner; + + public GtestSuiteRunner(Runner contained) { + mRunner = contained; + } + + public Description getDescription() { + return mRunner.getDescription(); + } + + public void run(RunNotifier notifier) { + long startTimeMillis = System.currentTimeMillis(); + mLogger.testCaseStarted(mRunner.getDescription(), + mRunner.getDescription().testCount()); + mRunner.run(notifier); + mLogger.testCaseFinished(mRunner.getDescription(), + mRunner.getDescription().testCount(), + System.currentTimeMillis() - startTimeMillis); + } + + public void filter(Filter filter) throws NoTestsRemainException { + if (mRunner instanceof Filterable) { + ((Filterable) mRunner).filter(filter); + } + } + } + + /** + * Returns a suite of unit tests with each class runner wrapped by a + * GtestSuiteRunner. + */ + @Override + public Runner getSuite(final RunnerBuilder builder, Class[] classes) + throws InitializationError { + return super.getSuite( + new RunnerBuilder() { + @Override + public Runner runnerForClass(Class testClass) throws Throwable { + return new GtestSuiteRunner(builder.runnerForClass(testClass)); + } + }, classes); + } + +} + diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestFilter.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestFilter.java new file mode 100644 index 0000000000..d5527aa22a --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestFilter.java @@ -0,0 +1,98 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.runner.Description; +import org.junit.runner.manipulation.Filter; + +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Filters tests based on a googletest-style filter string. + */ +class GtestFilter extends Filter { + + private final String mFilterString; + + private final Set mPositiveRegexes; + private final Set mNegativeRegexes; + + private static final Pattern ASTERISK = Pattern.compile("\\*"); + private static final Pattern COLON = Pattern.compile(":"); + private static final Pattern DASH = Pattern.compile("-"); + private static final Pattern DOLLAR = Pattern.compile("\\$"); + private static final Pattern PERIOD = Pattern.compile("\\."); + + /** + * Creates the filter and converts the provided googletest-style filter + * string into positive and negative regexes. + */ + public GtestFilter(String filterString) { + mFilterString = filterString; + mPositiveRegexes = new HashSet(); + mNegativeRegexes = new HashSet(); + + String[] filterStrings = COLON.split(filterString); + for (String f : filterStrings) { + if (f.isEmpty()) continue; + + String sanitized = PERIOD.matcher(f).replaceAll(Matcher.quoteReplacement("\\.")); + sanitized = DOLLAR.matcher(sanitized).replaceAll(Matcher.quoteReplacement("\\$")); + sanitized = ASTERISK.matcher(sanitized).replaceAll(".*"); + int negIndex = sanitized.indexOf('-'); + if (negIndex == 0) { + mNegativeRegexes.add(Pattern.compile(sanitized.substring(1))); + } else if (negIndex != -1) { + String[] c = DASH.split(sanitized, 2); + mPositiveRegexes.add(Pattern.compile(c[0])); + mNegativeRegexes.add(Pattern.compile(c[1])); + } else { + mPositiveRegexes.add(Pattern.compile(sanitized)); + } + } + } + + /** + * Determines whether or not a test with the provided description should + * run based on the configured positive and negative regexes. + * + * A test should run if: + * - it's just a class, OR + * - it doesn't match any of the negative regexes, AND + * - either: + * - there are no configured positive regexes, OR + * - it matches at least one of the positive regexes. + */ + @Override + public boolean shouldRun(Description description) { + if (description.getMethodName() == null) return true; + + String gtestName = description.getClassName() + "." + description.getMethodName(); + for (Pattern p : mNegativeRegexes) { + if (p.matcher(gtestName).matches()) return false; + } + + if (mPositiveRegexes.isEmpty()) return true; + + for (Pattern p : mPositiveRegexes) { + if (p.matcher(gtestName).matches()) return true; + } + + return false; + } + + /** + * Returns a description of this filter. + */ + @Override + public String describe() { + return "gtest-filter: " + mFilterString; + } + +} + diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestListener.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestListener.java new file mode 100644 index 0000000000..122d1355c0 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestListener.java @@ -0,0 +1,80 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.runner.Description; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +import java.util.HashSet; +import java.util.Set; + +/** A JUnit RunListener that emulates GTest output to the extent that it can. + */ +public class GtestListener extends RunListener { + + private Set mFailedTests; + private final GtestLogger mLogger; + private long mRunStartTimeMillis; + private long mTestStartTimeMillis; + private int mTestsPassed; + private boolean mCurrentTestPassed; + + public GtestListener(GtestLogger logger) { + mLogger = logger; + } + + /** Called before any tests run. + */ + @Override + public void testRunStarted(Description d) throws Exception { + mLogger.testRunStarted(d.testCount()); + mRunStartTimeMillis = System.currentTimeMillis(); + mTestsPassed = 0; + mFailedTests = new HashSet(); + mCurrentTestPassed = true; + } + + /** Called after all tests run. + */ + @Override + public void testRunFinished(Result r) throws Exception { + long elapsedTimeMillis = System.currentTimeMillis() - mRunStartTimeMillis; + mLogger.testRunFinished(mTestsPassed, mFailedTests, elapsedTimeMillis); + } + + /** Called when a test is about to start. + */ + @Override + public void testStarted(Description d) throws Exception { + mCurrentTestPassed = true; + mLogger.testStarted(d); + mTestStartTimeMillis = System.currentTimeMillis(); + } + + /** Called when a test has just finished. + */ + @Override + public void testFinished(Description d) throws Exception { + long testElapsedTimeMillis = System.currentTimeMillis() - mTestStartTimeMillis; + mLogger.testFinished(d, mCurrentTestPassed, testElapsedTimeMillis); + if (mCurrentTestPassed) { + ++mTestsPassed; + } else { + mFailedTests.add(d); + } + } + + /** Called when a test fails. + */ + @Override + public void testFailure(Failure f) throws Exception { + mCurrentTestPassed = false; + mLogger.testFailed(f); + } + +} + diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestLogger.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestLogger.java new file mode 100644 index 0000000000..09723745ca --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/GtestLogger.java @@ -0,0 +1,109 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.runner.Description; +import org.junit.runner.notification.Failure; + +import java.io.PrintStream; +import java.util.Set; + +/** + * Formats and logs test status information in googletest-style. + */ +public class GtestLogger { + + private final PrintStream mOutputStream; + + public GtestLogger(PrintStream outputStream) { + mOutputStream = outputStream; + } + + /** + * Logs the start of an individual test. + */ + public void testStarted(Description test) { + mOutputStream.format("[ RUN ] %s.%s", test.getClassName(), test.getMethodName()); + mOutputStream.println(); + } + + /** + * Logs a test failure. + */ + public void testFailed(Failure f) { + if (f.getException() != null) { + f.getException().printStackTrace(mOutputStream); + } + } + + /** + * Logs the end of an individual test. + */ + public void testFinished(Description test, boolean passed, long elapsedTimeMillis) { + if (passed) { + mOutputStream.format("[ OK ] %s.%s (%d ms)", + test.getClassName(), test.getMethodName(), elapsedTimeMillis); + } else { + mOutputStream.format("[ FAILED ] %s.%s (%d ms)", + test.getClassName(), test.getMethodName(), elapsedTimeMillis); + } + mOutputStream.println(); + } + + /** + * Logs the start of a test case. + */ + public void testCaseStarted(Description test, int testCount) { + mOutputStream.format("[----------] Run %d test cases from %s", testCount, + test.getClassName()); + mOutputStream.println(); + } + + /** + * Logs the end of a test case. + */ + public void testCaseFinished(Description test, int testCount, + long elapsedTimeMillis) { + mOutputStream.format("[----------] Run %d test cases from %s (%d ms)", + testCount, test.getClassName(), elapsedTimeMillis); + mOutputStream.println(); + mOutputStream.println(); + } + + /** + * Logs the start of a test run. + */ + public void testRunStarted(int testCount) { + mOutputStream.format("[==========] Running %d tests.", testCount); + mOutputStream.println(); + mOutputStream.println("[----------] Global test environment set-up."); + mOutputStream.println(); + } + + /** + * Logs the end of a test run. + */ + public void testRunFinished(int passedTestCount, Set failedTests, + long elapsedTimeMillis) { + int totalTestCount = passedTestCount + failedTests.size(); + mOutputStream.println("[----------] Global test environment tear-down."); + mOutputStream.format("[==========] %d tests ran. (%d ms total)", + totalTestCount, elapsedTimeMillis); + mOutputStream.println(); + mOutputStream.format("[ PASSED ] %d tests.", passedTestCount); + mOutputStream.println(); + if (!failedTests.isEmpty()) { + mOutputStream.format("[ FAILED ] %d tests.", failedTests.size()); + mOutputStream.println(); + for (Description d : failedTests) { + mOutputStream.format("[ FAILED ] %s.%s", d.getClassName(), d.getMethodName()); + mOutputStream.println(); + } + mOutputStream.println(); + } + } + +} + diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JsonListener.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JsonListener.java new file mode 100644 index 0000000000..3d501b3b18 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JsonListener.java @@ -0,0 +1,54 @@ +// Copyright 2015 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. + +package org.chromium.testing.local; + +import org.junit.runner.Description; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +/** A json RunListener that creates a Json file with test run information. + */ +public class JsonListener extends RunListener { + + private final JsonLogger mJsonLogger; + private long mTestStartTimeMillis; + private boolean mCurrentTestPassed; + + public JsonListener(JsonLogger jsonLogger) { + mJsonLogger = jsonLogger; + } + + /** Called after all tests run. + */ + @Override + public void testRunFinished(Result r) throws Exception { + mJsonLogger.writeJsonToFile(); + } + + /** Called when a test is about to start. + */ + @Override + public void testStarted(Description d) throws Exception { + mCurrentTestPassed = true; + mTestStartTimeMillis = System.currentTimeMillis(); + } + + /** Called when a test has just finished. + */ + @Override + public void testFinished(Description d) throws Exception { + long testElapsedTimeMillis = System.currentTimeMillis() - mTestStartTimeMillis; + mJsonLogger.addTestResultInfo(d, mCurrentTestPassed, testElapsedTimeMillis); + } + + /** Called when a test fails. + */ + @Override + public void testFailure(Failure f) throws Exception { + mCurrentTestPassed = false; + } +} + diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JsonLogger.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JsonLogger.java new file mode 100644 index 0000000000..c253ba33ca --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JsonLogger.java @@ -0,0 +1,89 @@ +// Copyright 2015 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. + +package org.chromium.testing.local; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.junit.runner.Description; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; + +/** + * Creates json file with junit test information. Format of the json file mirrors the + * json generated by build/android/pylib/results/json_results.py. + */ +public class JsonLogger { + + private final JSONObject mBaseJsonObject; + private final JSONObject mBaseTestInfoJsonObject; + private final File mOutputFile; + + public JsonLogger(File outputFile) { + mBaseJsonObject = new JSONObject(); + mBaseTestInfoJsonObject = new JSONObject(); + mOutputFile = outputFile; + + try { + mBaseJsonObject.put("global_tags", new JSONArray()); + mBaseJsonObject.put("all_tests", new JSONArray()); + mBaseJsonObject.put("disabled_tests", new JSONArray()); + mBaseJsonObject.put("per_iteration_data", + new JSONArray().put(mBaseTestInfoJsonObject)); + } catch (JSONException e) { + System.err.println("Unable to create json output."); + } + } + + /** + * Add the results of a test run to the json output. + */ + public void addTestResultInfo(Description test, boolean passed, long elapsedTimeMillis) { + JSONObject testInfoJsonObject = new JSONObject(); + + try { + testInfoJsonObject.put("status", (passed ? "SUCCESS" : "FAILURE")); + testInfoJsonObject.put("elapsed_time_ms", elapsedTimeMillis); + testInfoJsonObject.put("output_snippet", ""); + testInfoJsonObject.put("output_snippet_base64", ""); + testInfoJsonObject.put("losless_snippet", ""); + + if (mBaseTestInfoJsonObject.optJSONArray(testName(test)) == null) { + mBaseTestInfoJsonObject.put(testName(test), new JSONArray()); + mBaseJsonObject.getJSONArray("all_tests").put(testName(test)); + } + mBaseTestInfoJsonObject.getJSONArray(testName(test)).put(testInfoJsonObject); + } catch (JSONException e) { + System.err.println("Unable to log test to json output: " + testName(test)); + } + } + + /** + * Writes the json output to a file. + */ + public void writeJsonToFile() { + try { + PrintStream stream = new PrintStream(new FileOutputStream(mOutputFile)); + try { + stream.print(mBaseJsonObject); + } finally { + try { + stream.close(); + } catch (RuntimeException e) { + System.err.println("Unable to close output file: " + mOutputFile.getPath()); + } + } + } catch (FileNotFoundException e) { + System.err.println("File not found: " + mOutputFile.getPath()); + } + } + + private String testName(Description test) { + return test.getClassName() + "#" + test.getMethodName(); + } +} \ No newline at end of file diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java new file mode 100644 index 0000000000..20c5a657b5 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JunitTestArgParser.java @@ -0,0 +1,117 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * Parses command line arguments for JunitTestMain. + */ +public class JunitTestArgParser { + + private static final Pattern COLON = Pattern.compile(":"); + + private final Set mPackageFilters; + private final Set> mRunnerFilters; + private final Set mGtestFilters; + private File mJsonOutput; + private String[] mTestJars; + + public static JunitTestArgParser parse(String[] args) { + + JunitTestArgParser parsed = new JunitTestArgParser(); + + for (int i = 0; i < args.length; ++i) { + if (args[i].startsWith("-")) { + String argName; + if (args[i].startsWith("-", 1)) { + argName = args[i].substring(2, args[i].length()); + } else { + argName = args[i].substring(1, args[i].length()); + } + try { + if ("package-filter".equals(argName)) { + // Read the command line argument after the flag. + parsed.addPackageFilter(args[++i]); + } else if ("runner-filter".equals(argName)) { + // Read the command line argument after the flag. + parsed.addRunnerFilter(Class.forName(args[++i])); + } else if ("gtest-filter".equals(argName)) { + // Read the command line argument after the flag. + parsed.addGtestFilter(args[++i]); + } else if ("json-results-file".equals(argName)) { + // Read the command line argument after the flag. + parsed.setJsonOutputFile(args[++i]); + } else if ("test-jars".equals(argName)) { + // Read the command line argument after the flag. + parsed.setTestJars(args[++i]); + } else { + System.out.println("Ignoring flag: \"" + argName + "\""); + } + } catch (ArrayIndexOutOfBoundsException e) { + System.err.println("No value specified for argument \"" + argName + "\""); + System.exit(1); + } catch (ClassNotFoundException e) { + System.err.println("Class not found. (" + e.toString() + ")"); + System.exit(1); + } + } else { + System.out.println("Ignoring argument: \"" + args[i] + "\""); + } + } + + return parsed; + } + + private JunitTestArgParser() { + mPackageFilters = new HashSet(); + mRunnerFilters = new HashSet>(); + mGtestFilters = new HashSet(); + mJsonOutput = null; + } + + public Set getPackageFilters() { + return mPackageFilters; + } + + public Set> getRunnerFilters() { + return mRunnerFilters; + } + + public Set getGtestFilters() { + return mGtestFilters; + } + + public File getJsonOutputFile() { + return mJsonOutput; + } + + public String[] getTestJars() { + return mTestJars; + } + + private void addPackageFilter(String packageFilter) { + mPackageFilters.add(packageFilter); + } + + private void addRunnerFilter(Class runnerFilter) { + mRunnerFilters.add(runnerFilter); + } + + private void addGtestFilter(String gtestFilter) { + mGtestFilters.add(gtestFilter); + } + + private void setJsonOutputFile(String path) { + mJsonOutput = new File(path); + } + + private void setTestJars(String jars) { + mTestJars = COLON.split(jars); + } +} \ No newline at end of file diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java new file mode 100644 index 0000000000..f32579b9bf --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/JunitTestMain.java @@ -0,0 +1,109 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.regex.Pattern; + +/** + * Runs tests based on JUnit from the classpath on the host JVM based on the + * provided filter configurations. + */ +public final class JunitTestMain { + + private static final String CLASS_FILE_EXT = ".class"; + + private static final Pattern COLON = Pattern.compile(":"); + private static final Pattern FORWARD_SLASH = Pattern.compile("/"); + + private JunitTestMain() { + } + + /** + * Finds all classes on the class path annotated with RunWith. + */ + public static Class[] findClassesFromClasspath(String[] testJars) { + String[] jarPaths = COLON.split(System.getProperty("java.class.path")); + List testJarPaths = new ArrayList(testJars.length); + for (String testJar: testJars) { + for (String jarPath: jarPaths) { + if (jarPath.endsWith(testJar)) { + testJarPaths.add(jarPath); + break; + } + } + } + List classes = new ArrayList(); + for (String jp : testJarPaths) { + try { + JarFile jf = new JarFile(jp); + for (Enumeration eje = jf.entries(); eje.hasMoreElements();) { + JarEntry je = eje.nextElement(); + String cn = je.getName(); + if (!cn.endsWith(CLASS_FILE_EXT) || cn.indexOf('$') != -1) { + continue; + } + cn = cn.substring(0, cn.length() - CLASS_FILE_EXT.length()); + cn = FORWARD_SLASH.matcher(cn).replaceAll("."); + Class c = classOrNull(cn); + if (c != null && c.isAnnotationPresent(RunWith.class)) { + classes.add(c); + } + } + jf.close(); + } catch (IOException e) { + System.err.println("Error while reading classes from " + jp); + } + } + return classes.toArray(new Class[classes.size()]); + } + + private static Class classOrNull(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + System.err.println("Class not found: " + className); + } catch (NoClassDefFoundError e) { + System.err.println("Class definition not found: " + className); + } catch (Exception e) { + System.err.println("Other exception while reading class: " + className); + } + return null; + } + + public static void main(String[] args) { + JunitTestArgParser parser = JunitTestArgParser.parse(args); + + JUnitCore core = new JUnitCore(); + GtestLogger gtestLogger = new GtestLogger(System.out); + core.addListener(new GtestListener(gtestLogger)); + JsonLogger jsonLogger = new JsonLogger(parser.getJsonOutputFile()); + core.addListener(new JsonListener(jsonLogger)); + Class[] classes = findClassesFromClasspath(parser.getTestJars()); + Request testRequest = Request.classes(new GtestComputer(gtestLogger), classes); + + for (String packageFilter : parser.getPackageFilters()) { + testRequest = testRequest.filterWith(new PackageFilter(packageFilter)); + } + for (Class runnerFilter : parser.getRunnerFilters()) { + testRequest = testRequest.filterWith(new RunnerFilter(runnerFilter)); + } + for (String gtestFilter : parser.getGtestFilters()) { + testRequest = testRequest.filterWith(new GtestFilter(gtestFilter)); + } + System.exit(core.run(testRequest).wasSuccessful() ? 0 : 1); + } + +} + diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/LocalRobolectricTestRunner.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/LocalRobolectricTestRunner.java new file mode 100644 index 0000000000..cf6202b050 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/LocalRobolectricTestRunner.java @@ -0,0 +1,49 @@ +// Copyright 2015 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. + +package org.chromium.testing.local; + +import org.junit.runners.model.InitializationError; + +import org.robolectric.AndroidManifest; +import org.robolectric.DependencyResolver; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.SdkConfig; +import org.robolectric.annotation.Config; + +/** + * A custom Robolectric Junit4 Test Runner. This test runner will load the + * "real" android jars from a local directory rather than use Maven to fetch + * them from the Maven Central repository. Additionally, it will ignore the + * API level written in the AndroidManifest as that can cause issues if + * robolectric does not support that API level. + */ +public class LocalRobolectricTestRunner extends RobolectricTestRunner { + + private static final int ANDROID_API_LEVEL = 18; + + public LocalRobolectricTestRunner(Class testClass) throws InitializationError { + super(testClass); + } + + @Override + protected final DependencyResolver getJarResolver() { + return new RobolectricClasspathDependencyResolver(); + } + + @Override + protected SdkConfig pickSdkVersion(AndroidManifest appManifest, Config config) { + // Pulling from the manifest is dangerous as the apk might target a version of + // android that robolectric does not yet support. We still allow the API level to + // be overridden with the Config annotation. + return config.emulateSdk() < 0 + ? new SdkConfig(ANDROID_API_LEVEL) : super.pickSdkVersion(null, config); + } + + @Override + protected int pickReportedSdkVersion(Config config, AndroidManifest appManifest) { + return config.reportSdk() < 0 + ? ANDROID_API_LEVEL : super.pickReportedSdkVersion(config, appManifest); + } +} \ No newline at end of file diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/PackageFilter.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/PackageFilter.java new file mode 100644 index 0000000000..4d6a580d47 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/PackageFilter.java @@ -0,0 +1,41 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.runner.Description; +import org.junit.runner.manipulation.Filter; + +/** + * Filters tests based on the package. + */ +class PackageFilter extends Filter { + + private final String mFilterString; + + /** + * Creates the filter. + */ + public PackageFilter(String filterString) { + mFilterString = filterString; + } + + /** + * Determines whether or not a test with the provided description should + * run based on its package. + */ + @Override + public boolean shouldRun(Description description) { + return description.getTestClass().getPackage().getName().equals(mFilterString); + } + + /** + * Returns a description of this filter. + */ + @Override + public String describe() { + return "package-filter: " + mFilterString; + } + +} diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/RobolectricClasspathDependencyResolver.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/RobolectricClasspathDependencyResolver.java new file mode 100644 index 0000000000..47bc1ba0dd --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/RobolectricClasspathDependencyResolver.java @@ -0,0 +1,72 @@ +// Copyright 2015 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. + +package org.chromium.testing.local; + +import org.robolectric.DependencyJar; +import org.robolectric.DependencyResolver; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.regex.Pattern; + +/** + * A Robolectric dependency resolver that looks for the Robolectric dependencies + * in the Java classpath. + */ +public class RobolectricClasspathDependencyResolver implements DependencyResolver { + private static final Pattern COLON = Pattern.compile(":"); + private final String[] mClassPathJars; + + /** + * Creates a {@link ClasspathDependencyResolver}. + */ + public RobolectricClasspathDependencyResolver() { + mClassPathJars = COLON.split(System.getProperty("java.class.path")); + } + + /** + * Returns the {@link URL} for a Robolectric dependency. It looks through the jars + * in the classpath to find the dependency's filepath. + */ + @Override + public URL getLocalArtifactUrl(DependencyJar dependency) { + // Jar filenames are constructed identically to how they are built in Robolectric's + // own LocalDependencyResolver. + String dependencyJar = dependency.getArtifactId() + "-" + dependency.getVersion() + "." + + dependency.getType(); + + for (String jarPath : mClassPathJars) { + if (jarPath.endsWith(dependencyJar)) { + return fileToUrl(new File(jarPath)); + } + } + throw new IllegalStateException( + String.format("Robolectric jar %s was not found in classpath.", dependencyJar)); + } + + /** + * Returns the {@link URL} for a list of Robolectric dependencies. + */ + @Override + public URL[] getLocalArtifactUrls(DependencyJar... dependencies) { + URL[] urls = new URL[dependencies.length]; + + for (int i = 0; i < dependencies.length; i++) { + urls[i] = getLocalArtifactUrl(dependencies[i]); + } + + return urls; + } + + private static URL fileToUrl(File file) { + try { + return file.toURI().toURL(); + } catch (MalformedURLException e) { + throw new IllegalArgumentException( + String.format("File \"%s\" cannot be represented as a URL: %s", file, e)); + } + } +} \ No newline at end of file diff --git a/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/RunnerFilter.java b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/RunnerFilter.java new file mode 100644 index 0000000000..e32753e998 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/java/src/org/chromium/testing/local/RunnerFilter.java @@ -0,0 +1,45 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runner.manipulation.Filter; + +/** + * Filters tests based on the Runner class annotating the test class. + */ +class RunnerFilter extends Filter { + + private final Class mRunnerClass; + + /** + * Creates the filter. + */ + public RunnerFilter(Class runnerClass) { + mRunnerClass = runnerClass; + } + + /** + * Determines whether or not a test with the provided description should + * run based on the Runner class annotating the test class. + */ + @Override + public boolean shouldRun(Description description) { + Class c = description.getTestClass(); + return c != null && c.isAnnotationPresent(RunWith.class) + && c.getAnnotation(RunWith.class).value() == mRunnerClass; + } + + /** + * Returns a description of this filter. + */ + @Override + public String describe() { + return "runner-filter: " + mRunnerClass.getName(); + } + +} + diff --git a/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/GtestFilterTest.java b/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/GtestFilterTest.java new file mode 100644 index 0000000000..a19b37945d --- /dev/null +++ b/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/GtestFilterTest.java @@ -0,0 +1,124 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runner.manipulation.Filter; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * Unit tests for GtestFilter. + */ +@RunWith(BlockJUnit4ClassRunner.class) +public class GtestFilterTest { + + private class TestClass {} + private class OtherTestClass {} + + @Test + public void testDescription() { + Filter filterUnderTest = new GtestFilter(TestClass.class.getName() + ".*"); + Assert.assertEquals("gtest-filter: " + TestClass.class.getName() + ".*", + filterUnderTest.describe()); + } + + @Test + public void testNoFilter() { + Filter filterUnderTest = new GtestFilter(""); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "testMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "otherTestMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(OtherTestClass.class, "testMethod"))); + } + + @Test + public void testPositiveFilterExplicit() { + Filter filterUnderTest = new GtestFilter(TestClass.class.getName() + ".testMethod"); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "testMethod"))); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "otherTestMethod"))); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(OtherTestClass.class, "testMethod"))); + } + + @Test + public void testPositiveFilterClassRegex() { + Filter filterUnderTest = new GtestFilter(TestClass.class.getName() + ".*"); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "testMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "otherTestMethod"))); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(OtherTestClass.class, "testMethod"))); + } + + @Test + public void testNegativeFilterExplicit() { + Filter filterUnderTest = new GtestFilter("-" + TestClass.class.getName() + ".testMethod"); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "testMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "otherTestMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(OtherTestClass.class, "testMethod"))); + } + + @Test + public void testNegativeFilterClassRegex() { + Filter filterUnderTest = new GtestFilter("-" + TestClass.class.getName() + ".*"); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "testMethod"))); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "otherTestMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(OtherTestClass.class, "testMethod"))); + } + + @Test + public void testPositiveAndNegativeFilter() { + Filter filterUnderTest = new GtestFilter(TestClass.class.getName() + ".*" + + "-" + TestClass.class.getName() + ".testMethod"); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "testMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "otherTestMethod"))); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(OtherTestClass.class, "testMethod"))); + } + + @Test + public void testMultiplePositiveFilters() { + Filter filterUnderTest = new GtestFilter( + TestClass.class.getName() + ".otherTestMethod:" + + OtherTestClass.class.getName() + ".otherTestMethod"); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "testMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "otherTestMethod"))); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(OtherTestClass.class, "testMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(OtherTestClass.class, "otherTestMethod"))); + } + + @Test + public void testMultipleFiltersPositiveAndNegative() { + Filter filterUnderTest = new GtestFilter(TestClass.class.getName() + ".*:" + + "-" + TestClass.class.getName() + ".testMethod"); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "testMethod"))); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(TestClass.class, "otherTestMethod"))); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(OtherTestClass.class, "testMethod"))); + } +} + diff --git a/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/GtestLoggerTest.java b/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/GtestLoggerTest.java new file mode 100644 index 0000000000..0520901bcf --- /dev/null +++ b/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/GtestLoggerTest.java @@ -0,0 +1,146 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runners.BlockJUnit4ClassRunner; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.Serializable; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +/** + * Unit tests for GtestLogger. + */ +@RunWith(BlockJUnit4ClassRunner.class) +public class GtestLoggerTest { + + @Test + public void testTestStarted() { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + GtestLogger loggerUnderTest = new GtestLogger(new PrintStream(actual)); + loggerUnderTest.testStarted( + Description.createTestDescription(GtestLoggerTest.class, "testTestStarted")); + Assert.assertEquals( + "[ RUN ] org.chromium.testing.local.GtestLoggerTest.testTestStarted\n", + actual.toString()); + } + + @Test + public void testTestFinishedPassed() { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + GtestLogger loggerUnderTest = new GtestLogger(new PrintStream(actual)); + loggerUnderTest.testFinished( + Description.createTestDescription(GtestLoggerTest.class, "testTestFinishedPassed"), + true, 123); + Assert.assertEquals( + "[ OK ] org.chromium.testing.local.GtestLoggerTest.testTestFinishedPassed" + + " (123 ms)\n", + actual.toString()); + } + + @Test + public void testTestFinishedFailed() { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + GtestLogger loggerUnderTest = new GtestLogger(new PrintStream(actual)); + loggerUnderTest.testFinished( + Description.createTestDescription(GtestLoggerTest.class, "testTestFinishedPassed"), + false, 123); + Assert.assertEquals( + "[ FAILED ] org.chromium.testing.local.GtestLoggerTest.testTestFinishedPassed" + + " (123 ms)\n", + actual.toString()); + } + + @Test + public void testTestCaseStarted() { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + GtestLogger loggerUnderTest = new GtestLogger(new PrintStream(actual)); + loggerUnderTest.testCaseStarted( + Description.createSuiteDescription(GtestLoggerTest.class), 456); + Assert.assertEquals( + "[----------] Run 456 test cases from org.chromium.testing.local.GtestLoggerTest\n", + actual.toString()); + } + + @Test + public void testTestCaseFinished() { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + GtestLogger loggerUnderTest = new GtestLogger(new PrintStream(actual)); + loggerUnderTest.testCaseFinished( + Description.createSuiteDescription(GtestLoggerTest.class), 456, 123); + Assert.assertEquals( + "[----------] Run 456 test cases from org.chromium.testing.local.GtestLoggerTest" + + " (123 ms)\n\n", + actual.toString()); + } + + @Test + public void testTestRunStarted() { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + GtestLogger loggerUnderTest = new GtestLogger(new PrintStream(actual)); + loggerUnderTest.testRunStarted(1234); + Assert.assertEquals( + "[==========] Running 1234 tests.\n" + + "[----------] Global test environment set-up.\n\n", + actual.toString()); + } + + @Test + public void testTestRunFinishedNoFailures() { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + GtestLogger loggerUnderTest = new GtestLogger(new PrintStream(actual)); + loggerUnderTest.testRunFinished(1234, new HashSet(), 4321); + Assert.assertEquals( + "[----------] Global test environment tear-down.\n" + + "[==========] 1234 tests ran. (4321 ms total)\n" + + "[ PASSED ] 1234 tests.\n", + actual.toString()); + } + + @Test + public void testTestRunFinishedWithFailures() { + ByteArrayOutputStream actual = new ByteArrayOutputStream(); + GtestLogger loggerUnderTest = new GtestLogger(new PrintStream(actual)); + + Set failures = new TreeSet(new DescriptionComparator()); + failures.add(Description.createTestDescription( + GtestLoggerTest.class, "testTestRunFinishedNoFailures")); + failures.add(Description.createTestDescription( + GtestLoggerTest.class, "testTestRunFinishedWithFailures")); + + loggerUnderTest.testRunFinished(1232, failures, 4312); + Assert.assertEquals( + "[----------] Global test environment tear-down.\n" + + "[==========] 1234 tests ran. (4312 ms total)\n" + + "[ PASSED ] 1232 tests.\n" + + "[ FAILED ] 2 tests.\n" + + "[ FAILED ] org.chromium.testing.local.GtestLoggerTest" + + ".testTestRunFinishedNoFailures\n" + + "[ FAILED ] org.chromium.testing.local.GtestLoggerTest" + + ".testTestRunFinishedWithFailures\n" + + "\n", + actual.toString()); + } + + private static class DescriptionComparator implements Comparator, Serializable { + @Override + public int compare(Description o1, Description o2) { + return toGtestStyleString(o1).compareTo(toGtestStyleString(o2)); + } + + private static String toGtestStyleString(Description d) { + return d.getClassName() + "." + d.getMethodName(); + } + } +} + diff --git a/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/PackageFilterTest.java b/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/PackageFilterTest.java new file mode 100644 index 0000000000..4427676bdb --- /dev/null +++ b/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/PackageFilterTest.java @@ -0,0 +1,47 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runner.manipulation.Filter; +import org.junit.runners.BlockJUnit4ClassRunner; + +/** + * Unit tests for PackageFilter. + */ +@RunWith(BlockJUnit4ClassRunner.class) +public class PackageFilterTest { + + @Test + public void testDescription() { + Filter filterUnderTest = new PackageFilter("test.package"); + Assert.assertEquals("package-filter: test.package", filterUnderTest.describe()); + } + + @Test + public void testNoFilter() { + Filter filterUnderTest = new PackageFilter(""); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(PackageFilterTest.class, "testNoFilter"))); + } + + @Test + public void testFilterHit() { + Filter filterUnderTest = new PackageFilter("org.chromium.testing.local"); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(PackageFilterTest.class, "testWithFilter"))); + } + + @Test + public void testFilterMiss() { + Filter filterUnderTest = new PackageFilter("org.chromium.native_test"); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(PackageFilterTest.class, "testWithFilter"))); + } + +} diff --git a/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/RunnerFilterTest.java b/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/RunnerFilterTest.java new file mode 100644 index 0000000000..9f54bfa685 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/javatests/src/org/chromium/testing/local/RunnerFilterTest.java @@ -0,0 +1,58 @@ +// Copyright 2014 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. + +package org.chromium.testing.local; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runner.manipulation.Filter; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.Suite; + +/** + * Unit tests for RunnerFilter. + */ +@RunWith(BlockJUnit4ClassRunner.class) +public class RunnerFilterTest { + + private class FakeTestClass {} + + @Test + public void testDescription() { + Filter filterUnderTest = new RunnerFilter(BlockJUnit4ClassRunner.class); + Assert.assertEquals("runner-filter: org.junit.runners.BlockJUnit4ClassRunner", + filterUnderTest.describe()); + } + + @Test + public void testNoFilter() { + Filter filterUnderTest = new RunnerFilter(null); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(RunnerFilterTest.class, "testNoFilter"))); + } + + @Test + public void testFilterHit() { + Filter filterUnderTest = new RunnerFilter(BlockJUnit4ClassRunner.class); + Assert.assertTrue(filterUnderTest.shouldRun( + Description.createTestDescription(RunnerFilterTest.class, "testFilterHit"))); + } + + @Test + public void testFilterMiss() { + Filter filterUnderTest = new RunnerFilter(Suite.class); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(RunnerFilterTest.class, "testFilterMiss"))); + } + + @Test + public void testClassNotFound() { + Filter filterUnderTest = new RunnerFilter(BlockJUnit4ClassRunner.class); + Assert.assertFalse(filterUnderTest.shouldRun( + Description.createTestDescription(FakeTestClass.class, "fakeTestMethod"))); + } +} + diff --git a/engine/src/flutter/testing/android/junit/junit_test.gyp b/engine/src/flutter/testing/android/junit/junit_test.gyp new file mode 100644 index 0000000000..16e00488d1 --- /dev/null +++ b/engine/src/flutter/testing/android/junit/junit_test.gyp @@ -0,0 +1,43 @@ +# Copyright 2014 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. + +{ + 'targets': [ + { + # GN: //testing/android/junit:junit_test_support + 'target_name': 'junit_test_support', + 'type': 'none', + 'dependencies': [ + '../../../third_party/junit/junit.gyp:junit_jar', + '../../../third_party/mockito/mockito.gyp:mockito_jar', + '../../../third_party/robolectric/robolectric.gyp:robolectric_jar' + ], + 'variables': { + 'src_paths': [ + 'java/src', + ], + }, + 'includes': [ + '../../../build/host_jar.gypi', + ], + }, + { + # GN: //testing/android/junit:junit_unittests + 'target_name': 'junit_unit_tests', + 'type': 'none', + 'dependencies': [ + 'junit_test_support', + ], + 'variables': { + 'main_class': 'org.chromium.testing.local.JunitTestMain', + 'src_paths': [ + 'javatests/src', + ], + }, + 'includes': [ + '../../../build/host_jar.gypi', + ], + }, + ], +} diff --git a/engine/src/flutter/testing/android/native_test.gyp b/engine/src/flutter/testing/android/native_test.gyp new file mode 100644 index 0000000000..fcfd7d7028 --- /dev/null +++ b/engine/src/flutter/testing/android/native_test.gyp @@ -0,0 +1,71 @@ +# Copyright (c) 2012 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. + +{ + 'conditions': [ + ['OS=="android"', { + 'targets': [ + { + # GN: //testing/android:native_test_jni_headers + 'target_name': 'native_test_jni_headers', + 'type': 'none', + 'sources': [ + 'native_test/java/src/org/chromium/native_test/NativeTestActivity.java' + ], + 'variables': { + 'jni_gen_package': 'testing', + }, + 'includes': [ '../../build/jni_generator.gypi' ], + }, + { + # GN: //testing/android:native_test_support + 'target_name': 'native_test_support', + 'message': 'building native pieces of native test package', + 'type': 'static_library', + 'sources': [ + 'native_test/native_test_launcher.cc', + 'native_test/native_test_launcher.h', + 'native_test/native_test_util.cc', + 'native_test/native_test_util.h', + ], + 'dependencies': [ + '../../base/base.gyp:base', + '../../base/base.gyp:test_support_base', + '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../gtest.gyp:gtest', + 'native_test_jni_headers', + ], + }, + { + # GN: //testing/android:native_test_native_code + 'target_name': 'native_test_native_code', + 'message': 'building JNI onload for native test package', + 'type': 'static_library', + 'sources': [ + 'native_test/native_test_jni_onload.cc', + ], + 'dependencies': [ + 'native_test_support', + '../../base/base.gyp:base', + ], + }, + { + 'target_name': 'native_test_java', + 'type': 'none', + 'dependencies': [ + 'appurify_support.gyp:appurify_support_java', + '../../base/base.gyp:base_native_libraries_gen', + '../../base/base.gyp:base_java', + ], + 'variables': { + 'chromium_code': '1', + 'jar_excluded_classes': [ '*/NativeLibraries.class' ], + 'java_in_dir': 'native_test/java', + }, + 'includes': [ '../../build/java.gypi' ], + }, + ], + }] + ], +} diff --git a/engine/src/flutter/testing/android/native_test/BUILD.gn b/engine/src/flutter/testing/android/native_test/BUILD.gn new file mode 100644 index 0000000000..0e8ffcb581 --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/BUILD.gn @@ -0,0 +1,44 @@ +# Copyright 2014 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. + +import("//build/config/android/rules.gni") + +# GYP: //testing/android/native_test.gyp:native_test_support +source_set("native_test_support") { + testonly = true + sources = [ + "native_test_launcher.cc", + "native_test_launcher.h", + "native_test_util.cc", + "native_test_util.h", + ] + deps = [ + ":native_test_jni_headers", + "//base", + "//base/test:test_support", + "//base/third_party/dynamic_annotations", + "//testing/gtest", + ] +} + +# GYP: //testing/android/native_test.gyp:native_test_native_code +source_set("native_test_native_code") { + testonly = true + sources = [ + "native_test_jni_onload.cc", + ] + libs = [ "log" ] + deps = [ + ":native_test_support", + "//base", + ] +} + +# GYP: //testing/android/native_test.gyp:native_test_jni_headers +generate_jni("native_test_jni_headers") { + sources = [ + "java/src/org/chromium/native_test/NativeTestActivity.java", + ] + jni_package = "testing" +} diff --git a/engine/src/flutter/testing/android/native_test/README.chromium b/engine/src/flutter/testing/android/native_test/README.chromium new file mode 100644 index 0000000000..c00255a43a --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/README.chromium @@ -0,0 +1,2 @@ +apk-based runner for Chromium unit test bundles. This is a simple wrapper APK +that should include a single gtest native library. diff --git a/engine/src/flutter/testing/android/native_test/java/AndroidManifest.xml b/engine/src/flutter/testing/android/native_test/java/AndroidManifest.xml new file mode 100644 index 0000000000..6505014428 --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/java/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java b/engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java new file mode 100644 index 0000000000..ac2ddeef84 --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeBrowserTestActivity.java @@ -0,0 +1,46 @@ +// Copyright 2015 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. + +package org.chromium.native_test; + +import android.os.Bundle; + +/** + * An {@link android.app.Activity} for running native browser tests. + */ +public abstract class NativeBrowserTestActivity extends NativeTestActivity { + + private static final String BROWSER_TESTS_FLAGS[] = { + // content::kSingleProcessTestsFlag + "--single_process", + + // switches::kUseFakeDeviceForMediaStream + "--use-fake-device-for-media-stream", + + // switches::kUseFakeUIForMediaStream + "--use-fake-ui-for-media-stream" + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + for (String flag : BROWSER_TESTS_FLAGS) { + appendCommandLineFlags(flag); + } + } + + @Override + public void onStart() { + initializeBrowserProcess(); + super.onStart(); + } + + /** Initializes the browser process. + * + * This generally includes loading native libraries and switching to the native command line, + * among other things. + */ + protected abstract void initializeBrowserProcess(); + +} diff --git a/engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeTestActivity.java b/engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeTestActivity.java new file mode 100644 index 0000000000..35a79f8c4f --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeTestActivity.java @@ -0,0 +1,121 @@ +// Copyright 2012 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. + +package org.chromium.native_test; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; + +import org.chromium.base.CommandLine; +import org.chromium.base.JNINamespace; +import org.chromium.base.Log; + +import java.io.File; + +/** + * Android's NativeActivity is mostly useful for pure-native code. + * Our tests need to go up to our own java classes, which is not possible using + * the native activity class loader. + */ +@JNINamespace("testing::android") +public class NativeTestActivity extends Activity { + public static final String EXTRA_COMMAND_LINE_FILE = + "org.chromium.native_test.NativeTestActivity.CommandLineFile"; + public static final String EXTRA_COMMAND_LINE_FLAGS = + "org.chromium.native_test.NativeTestActivity.CommandLineFlags"; + public static final String EXTRA_STDOUT_FILE = + "org.chromium.native_test.NativeTestActivity.StdoutFile"; + + private static final String TAG = "cr.native_test"; + private static final String EXTRA_RUN_IN_SUB_THREAD = "RunInSubThread"; + // We post a delayed task to run tests so that we do not block onCreate(). + private static final long RUN_TESTS_DELAY_IN_MS = 300; + + private String mCommandLineFilePath; + private StringBuilder mCommandLineFlags = new StringBuilder(); + private boolean mRunInSubThread = false; + private boolean mStdoutFifo = false; + private String mStdoutFilePath; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + CommandLine.init(new String[]{}); + + parseArgumentsFromIntent(getIntent()); + } + + private void parseArgumentsFromIntent(Intent intent) { + mCommandLineFilePath = intent.getStringExtra(EXTRA_COMMAND_LINE_FILE); + if (mCommandLineFilePath == null) { + mCommandLineFilePath = ""; + } else { + File commandLineFile = new File(mCommandLineFilePath); + if (!commandLineFile.isAbsolute()) { + mCommandLineFilePath = Environment.getExternalStorageDirectory() + "/" + + mCommandLineFilePath; + } + Log.i(TAG, "command line file path: %s", mCommandLineFilePath); + } + + String commandLineFlags = intent.getStringExtra(EXTRA_COMMAND_LINE_FLAGS); + if (commandLineFlags != null) mCommandLineFlags.append(commandLineFlags); + + mRunInSubThread = intent.hasExtra(EXTRA_RUN_IN_SUB_THREAD); + + mStdoutFilePath = intent.getStringExtra(EXTRA_STDOUT_FILE); + if (mStdoutFilePath == null) { + mStdoutFilePath = new File(getFilesDir(), "test.fifo").getAbsolutePath(); + mStdoutFifo = true; + } + } + + protected void appendCommandLineFlags(String flags) { + mCommandLineFlags.append(" ").append(flags); + } + + @Override + public void onStart() { + super.onStart(); + + if (mRunInSubThread) { + // Create a new thread and run tests on it. + new Thread() { + @Override + public void run() { + runTests(); + } + }.start(); + } else { + // Post a task to run the tests. This allows us to not block + // onCreate and still run tests on the main thread. + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + runTests(); + } + }, RUN_TESTS_DELAY_IN_MS); + } + } + + private void runTests() { + nativeRunTests(mCommandLineFlags.toString(), mCommandLineFilePath, mStdoutFilePath, + mStdoutFifo, getApplicationContext()); + finish(); + } + + // Signal a failure of the native test loader to python scripts + // which run tests. For example, we look for + // RUNNER_FAILED build/android/test_package.py. + private void nativeTestFailed() { + Log.e(TAG, "[ RUNNER_FAILED ] could not load native library"); + } + + private native void nativeRunTests(String commandLineFlags, String commandLineFilePath, + String stdoutFilePath, boolean stdoutFifo, Context appContext); +} diff --git a/engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java b/engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java new file mode 100644 index 0000000000..5c0cb9308e --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/java/src/org/chromium/native_test/NativeTestInstrumentationTestRunner.java @@ -0,0 +1,176 @@ +// Copyright 2014 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. + +package org.chromium.native_test; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; + +import org.chromium.base.Log; +import org.chromium.test.support.ResultsBundleGenerator; +import org.chromium.test.support.RobotiumBundleGenerator; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * An Instrumentation that runs tests based on NativeTestActivity. + */ +public class NativeTestInstrumentationTestRunner extends Instrumentation { + + public static final String EXTRA_NATIVE_TEST_ACTIVITY = + "org.chromium.native_test.NativeTestInstrumentationTestRunner." + + "NativeTestActivity"; + + private static final String TAG = "cr.native_test"; + + private static final int ACCEPT_TIMEOUT_MS = 5000; + private static final String DEFAULT_NATIVE_TEST_ACTIVITY = + "org.chromium.native_test.NativeUnitTestActivity"; + private static final Pattern RE_TEST_OUTPUT = Pattern.compile("\\[ *([^ ]*) *\\] ?([^ ]+) .*"); + + private ResultsBundleGenerator mBundleGenerator = new RobotiumBundleGenerator(); + private String mCommandLineFile; + private String mCommandLineFlags; + private String mNativeTestActivity; + private Bundle mLogBundle = new Bundle(); + private File mStdoutFile; + + @Override + public void onCreate(Bundle arguments) { + mCommandLineFile = arguments.getString(NativeTestActivity.EXTRA_COMMAND_LINE_FILE); + mCommandLineFlags = arguments.getString(NativeTestActivity.EXTRA_COMMAND_LINE_FLAGS); + mNativeTestActivity = arguments.getString(EXTRA_NATIVE_TEST_ACTIVITY); + if (mNativeTestActivity == null) mNativeTestActivity = DEFAULT_NATIVE_TEST_ACTIVITY; + + try { + mStdoutFile = File.createTempFile( + ".temp_stdout_", ".txt", Environment.getExternalStorageDirectory()); + Log.i(TAG, "stdout file created: %s", mStdoutFile.getAbsolutePath()); + } catch (IOException e) { + Log.e(TAG, "Unable to create temporary stdout file.", e); + finish(Activity.RESULT_CANCELED, new Bundle()); + return; + } + start(); + } + + @Override + public void onStart() { + super.onStart(); + Bundle results = runTests(); + finish(Activity.RESULT_OK, results); + } + + /** Runs the tests in the NativeTestActivity and returns a Bundle containing the results. + */ + private Bundle runTests() { + Log.i(TAG, "Creating activity."); + Activity activityUnderTest = startNativeTestActivity(); + + Log.i(TAG, "Waiting for tests to finish."); + try { + while (!activityUnderTest.isFinishing()) { + Thread.sleep(100); + } + } catch (InterruptedException e) { + Log.e(TAG, "Interrupted while waiting for activity to be destroyed: ", e); + } + + Log.i(TAG, "Getting results."); + Map results = parseResults(activityUnderTest); + + Log.i(TAG, "Parsing results and generating output."); + return mBundleGenerator.generate(results); + } + + /** Starts the NativeTestActivty. + */ + private Activity startNativeTestActivity() { + Intent i = new Intent(Intent.ACTION_MAIN); + i.setComponent(new ComponentName(getContext().getPackageName(), mNativeTestActivity)); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (mCommandLineFile != null) { + Log.i(TAG, "Passing command line file extra: %s", mCommandLineFile); + i.putExtra(NativeTestActivity.EXTRA_COMMAND_LINE_FILE, mCommandLineFile); + } + if (mCommandLineFlags != null) { + Log.i(TAG, "Passing command line flag extra: %s", mCommandLineFlags); + i.putExtra(NativeTestActivity.EXTRA_COMMAND_LINE_FLAGS, mCommandLineFlags); + } + i.putExtra(NativeTestActivity.EXTRA_STDOUT_FILE, mStdoutFile.getAbsolutePath()); + return startActivitySync(i); + } + + /** + * Generates a map between test names and test results from the instrumented Activity's + * output. + */ + private Map parseResults( + Activity activityUnderTest) { + Map results = + new HashMap(); + + BufferedReader r = null; + + try { + if (mStdoutFile == null || !mStdoutFile.exists()) { + Log.e(TAG, "Unable to find stdout file."); + return results; + } + + r = new BufferedReader(new InputStreamReader( + new BufferedInputStream(new FileInputStream(mStdoutFile)))); + + for (String l = r.readLine(); l != null && !l.equals("< register_callbacks; + register_callbacks.push_back(base::Bind(&RegisterJNI)); + + if (!base::android::OnJNIOnLoadRegisterJNI(vm, register_callbacks)) + return -1; + + std::vector init_callbacks; + init_callbacks.push_back(base::Bind(&Init)); + if (!base::android::OnJNIOnLoadInit(init_callbacks)) + return -1; + + return JNI_VERSION_1_4; +} diff --git a/engine/src/flutter/testing/android/native_test/native_test_launcher.cc b/engine/src/flutter/testing/android/native_test/native_test_launcher.cc new file mode 100644 index 0000000000..343f1180c6 --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/native_test_launcher.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2012 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. + +// This class sets up the environment for running the native tests inside an +// android application. It outputs (to a fifo) markers identifying the +// START/PASSED/CRASH of the test suite, FAILURE/SUCCESS of individual tests, +// etc. +// These markers are read by the test runner script to generate test results. +// It installs signal handlers to detect crashes. + +#include +#include + +#include "base/android/base_jni_registrar.h" +#include "base/android/fifo_utils.h" +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" +#include "base/at_exit.h" +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "jni/NativeTestActivity_jni.h" +#include "testing/android/native_test/native_test_util.h" + +// The main function of the program to be wrapped as a test apk. +extern int main(int argc, char** argv); + +namespace testing { +namespace android { + +namespace { + +// The test runner script writes the command line file in +// "/data/local/tmp". +static const char kCommandLineFilePath[] = + "/data/local/tmp/chrome-native-tests-command-line"; + +const char kLogTag[] = "chromium"; +const char kCrashedMarker[] = "[ CRASHED ]\n"; + +// The list of signals which are considered to be crashes. +const int kExceptionSignals[] = { + SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1 +}; + +struct sigaction g_old_sa[NSIG]; + +// This function runs in a compromised context. It should not allocate memory. +void SignalHandler(int sig, siginfo_t* info, void* reserved) { + // Output the crash marker. + write(STDOUT_FILENO, kCrashedMarker, sizeof(kCrashedMarker)); + g_old_sa[sig].sa_sigaction(sig, info, reserved); +} + +// Writes printf() style string to Android's logger where |priority| is one of +// the levels defined in . +void AndroidLog(int priority, const char* format, ...) { + va_list args; + va_start(args, format); + __android_log_vprint(priority, kLogTag, format, args); + va_end(args); +} + +} // namespace + +static void RunTests(JNIEnv* env, + jobject obj, + jstring jcommand_line_flags, + jstring jcommand_line_file_path, + jstring jstdout_file_path, + jboolean jstdout_fifo, + jobject app_context) { + // Command line initialized basically, will be fully initialized later. + static const char* const kInitialArgv[] = { "ChromeTestActivity" }; + base::CommandLine::Init(arraysize(kInitialArgv), kInitialArgv); + + // Set the application context in base. + base::android::ScopedJavaLocalRef scoped_context( + env, env->NewLocalRef(app_context)); + base::android::InitApplicationContext(env, scoped_context); + base::android::RegisterJni(env); + + std::vector args; + + const std::string command_line_file_path( + base::android::ConvertJavaStringToUTF8(env, jcommand_line_file_path)); + if (command_line_file_path.empty()) + ParseArgsFromCommandLineFile(kCommandLineFilePath, &args); + else + ParseArgsFromCommandLineFile(command_line_file_path.c_str(), &args); + + const std::string command_line_flags( + base::android::ConvertJavaStringToUTF8(env, jcommand_line_flags)); + ParseArgsFromString(command_line_flags, &args); + + std::vector argv; + int argc = ArgsToArgv(args, &argv); + + // Fully initialize command line with arguments. + base::CommandLine::ForCurrentProcess()->AppendArguments( + base::CommandLine(argc, &argv[0]), false); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + base::FilePath stdout_file_path( + base::android::ConvertJavaStringToUTF8(env, jstdout_file_path)); + + // A few options, such "--gtest_list_tests", will just use printf directly + // Always redirect stdout to a known file. + unlink(stdout_file_path.value().c_str()); + if (jstdout_fifo) { + if (!base::android::CreateFIFO(stdout_file_path, 0666)) { + AndroidLog(ANDROID_LOG_ERROR, "Failed to create fifo %s: %s\n", + stdout_file_path.value().c_str(), strerror(errno)); + exit(EXIT_FAILURE); + } + } + if (!base::android::RedirectStream(stdout, stdout_file_path, "w+")) { + AndroidLog(ANDROID_LOG_ERROR, "Failed to redirect stream to file: %s: %s\n", + stdout_file_path.value().c_str(), strerror(errno)); + exit(EXIT_FAILURE); + } + dup2(STDOUT_FILENO, STDERR_FILENO); + + if (command_line.HasSwitch(switches::kWaitForDebugger)) { + AndroidLog(ANDROID_LOG_VERBOSE, + "Native test waiting for GDB because flag %s was supplied", + switches::kWaitForDebugger); + base::debug::WaitForDebugger(24 * 60 * 60, false); + } + + ScopedMainEntryLogger scoped_main_entry_logger; + main(argc, &argv[0]); +} + +bool RegisterNativeTestJNI(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +// TODO(nileshagrawal): now that we're using FIFO, test scripts can detect EOF. +// Remove the signal handlers. +void InstallHandlers() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_SIGINFO; + + for (unsigned int i = 0; kExceptionSignals[i] != -1; ++i) { + sigaction(kExceptionSignals[i], &sa, &g_old_sa[kExceptionSignals[i]]); + } +} + +} // namespace android +} // namespace testing diff --git a/engine/src/flutter/testing/android/native_test/native_test_launcher.h b/engine/src/flutter/testing/android/native_test/native_test_launcher.h new file mode 100644 index 0000000000..ea4dfe837a --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/native_test_launcher.h @@ -0,0 +1,19 @@ +// Copyright 2015 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. + +#ifndef TESTING_ANDROID_NATIVE_TEST_LAUNCHER_H_ +#define TESTING_ANDROID_NATIVE_TEST_LAUNCHER_H_ + +#include + +namespace testing { +namespace android { + +void InstallHandlers(); +bool RegisterNativeTestJNI(JNIEnv* env); + +} // namespace android +} // namespace testing + +#endif // TESTING_ANDROID_NATIVE_TEST_LAUNCHER_H_ diff --git a/engine/src/flutter/testing/android/native_test/native_test_util.cc b/engine/src/flutter/testing/android/native_test/native_test_util.cc new file mode 100644 index 0000000000..ffb1b114e4 --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/native_test_util.cc @@ -0,0 +1,50 @@ +// 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. + +#include "testing/android/native_test/native_test_util.h" + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/strings/string_tokenizer.h" +#include "base/strings/string_util.h" + +namespace testing { +namespace android { + +void ParseArgsFromString(const std::string& command_line, + std::vector* args) { + base::StringTokenizer tokenizer(command_line, base::kWhitespaceASCII); + tokenizer.set_quote_chars("\""); + while (tokenizer.GetNext()) { + std::string token; + base::RemoveChars(tokenizer.token(), "\"", &token); + args->push_back(token); + } +} + +void ParseArgsFromCommandLineFile( + const char* path, std::vector* args) { + base::FilePath command_line(path); + std::string command_line_string; + if (base::ReadFileToString(command_line, &command_line_string)) { + ParseArgsFromString(command_line_string, args); + } +} + +int ArgsToArgv(const std::vector& args, + std::vector* argv) { + // We need to pass in a non-const char**. + int argc = args.size(); + + argv->resize(argc + 1); + for (int i = 0; i < argc; ++i) { + (*argv)[i] = const_cast(args[i].c_str()); + } + (*argv)[argc] = NULL; // argv must be NULL terminated. + + return argc; +} + +} // namespace android +} // namespace testing diff --git a/engine/src/flutter/testing/android/native_test/native_test_util.h b/engine/src/flutter/testing/android/native_test/native_test_util.h new file mode 100644 index 0000000000..e647a92201 --- /dev/null +++ b/engine/src/flutter/testing/android/native_test/native_test_util.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef TESTING_ANDROID_NATIVE_TEST_UTIL_ +#define TESTING_ANDROID_NATIVE_TEST_UTIL_ + +#include +#include +#include + +// Helper methods for setting up environment for running gtest tests +// inside an APK. +namespace testing { +namespace android { + +class ScopedMainEntryLogger { + public: + ScopedMainEntryLogger() { + printf(">>ScopedMainEntryLogger\n"); + } + + ~ScopedMainEntryLogger() { + printf("<* args); +void ParseArgsFromCommandLineFile( + const char* path, std::vector* args); +int ArgsToArgv(const std::vector& args, std::vector* argv); + +} // namespace android +} // namespace testing + +#endif // TESTING_ANDROID_NATIVE_TEST_UTIL_ diff --git a/engine/src/flutter/testing/android/on_device_instrumentation.gyp b/engine/src/flutter/testing/android/on_device_instrumentation.gyp new file mode 100644 index 0000000000..12e0f353c1 --- /dev/null +++ b/engine/src/flutter/testing/android/on_device_instrumentation.gyp @@ -0,0 +1,79 @@ +# Copyright 2015 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. + +{ + 'conditions': [ + ['OS=="android"', { + 'variables' : { + 'driver_apk_name': 'OnDeviceInstrumentationDriver', + 'driver_apk_path': '<(PRODUCT_DIR)/apks/<(driver_apk_name).apk' + }, + 'targets': [ + { + 'target_name': 'reporter_java', + 'type': 'none', + 'dependencies': ['../../base/base.gyp:base_java'], + 'variables': { + 'java_in_dir': '../../testing/android/reporter/java', + }, + 'includes': [ + '../../build/java.gypi', + ], + }, + { + 'target_name': 'broker_java', + 'type': 'none', + 'variables': { + 'java_in_dir': '../../testing/android/broker/java', + }, + 'includes': [ + '../../build/java.gypi', + ], + }, + { + 'target_name': 'driver_apk', + 'type': 'none', + 'dependencies': [ + 'broker_java', + 'reporter_java', + 'appurify_support.gyp:appurify_support_java', + ], + 'variables': { + 'apk_name': '<(driver_apk_name)', + 'final_apk_path': '<(driver_apk_path)', + 'java_in_dir': '../../testing/android/driver/java', + }, + 'includes': [ + '../../build/java_apk.gypi', + ], + }, + { + # This emulates gn's datadeps fields, allowing other APKs to declare + # that they require that this APK be built without including the + # driver's code. + 'target_name': 'require_driver_apk', + 'type': 'none', + 'actions': [ + { + 'action_name': 'require_<(driver_apk_name)', + 'message': 'Making sure <(driver_apk_path) has been built.', + 'variables': { + 'required_file': '<(PRODUCT_DIR)/driver_apk/<(driver_apk_name).apk.required', + }, + 'inputs': [ + '<(driver_apk_path)', + ], + 'outputs': [ + '<(required_file)', + ], + 'action': [ + 'python', '../../build/android/gyp/touch.py', '<(required_file)', + ], + }, + ], + }, + ], + }], + ], +} diff --git a/engine/src/flutter/testing/android/reporter/BUILD.gn b/engine/src/flutter/testing/android/reporter/BUILD.gn new file mode 100644 index 0000000000..7086a79d3d --- /dev/null +++ b/engine/src/flutter/testing/android/reporter/BUILD.gn @@ -0,0 +1,19 @@ +# Copyright 2015 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. + +import("//build/config/android/rules.gni") + +# GYP: //testing/android/on_device_instrumentation.gyp:reporter_java +android_library("reporter_java") { + chromium_code = true + + deps = [ + "//base:base_java", + ] + java_files = [ + "java/src/org/chromium/test/reporter/TestStatusListener.java", + "java/src/org/chromium/test/reporter/TestStatusReceiver.java", + "java/src/org/chromium/test/reporter/TestStatusReporter.java", + ] +} diff --git a/engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusListener.java b/engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusListener.java new file mode 100644 index 0000000000..62754181ce --- /dev/null +++ b/engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusListener.java @@ -0,0 +1,78 @@ +// Copyright 2015 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. + +package org.chromium.test.reporter; + +import android.content.Context; +import android.util.Log; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestListener; + +/** + * A TestListener that reports when tests start, pass, or fail. + */ +public class TestStatusListener implements TestListener { + + private static final String TAG = "TestStatusListener"; + + private boolean mFailed; + private final TestStatusReporter mReporter; + + public TestStatusListener(Context context) { + mReporter = new TestStatusReporter(context); + } + + /** Called when an error has occurred while running a test. + + Note that an error usually means a problem with the test or test harness, not with + the code under test. + + @param test The test in which the error occurred. + @param t The exception that was raised. + */ + @Override + public void addError(Test test, Throwable t) { + Log.e(TAG, "Error while running " + test.toString(), t); + mFailed = true; + } + + /** Called when a test has failed. + + @param test The test in which the failure occurred. + @param t The exception that was raised. + */ + public void addFailure(Test test, AssertionFailedError e) { + Log.e(TAG, "Failure while running " + test.toString(), e); + mFailed = true; + } + + /** Called when a test has started. + @param test The test that started. + */ + @Override + public void startTest(Test test) { + mFailed = false; + TestCase testCase = (TestCase) test; + mReporter.startHeartbeat(); + mReporter.testStarted(testCase.getClass().getName(), testCase.getName()); + } + + /** Called when a test has ended. + @param test The test that ended. + */ + @Override + public void endTest(Test test) { + TestCase testCase = (TestCase) test; + if (mFailed) { + mReporter.testFailed(testCase.getClass().getName(), testCase.getName()); + } else { + mReporter.testPassed(testCase.getClass().getName(), testCase.getName()); + } + mReporter.stopHeartbeat(); + } + +} diff --git a/engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusReceiver.java b/engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusReceiver.java new file mode 100644 index 0000000000..e4af9b652e --- /dev/null +++ b/engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusReceiver.java @@ -0,0 +1,128 @@ +// Copyright 2015 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. + +package org.chromium.test.reporter; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** Receives test status broadcasts send from + {@link org.chromium.test.reporter.TestStatusReporter}. + */ +public class TestStatusReceiver extends BroadcastReceiver { + + private static final String TAG = "ResultReceiver"; + + private final List mFailCallbacks = new ArrayList(); + private final List mHeartbeatCallbacks = new ArrayList(); + private final List mPassCallbacks = new ArrayList(); + private final List mStartCallbacks = new ArrayList(); + + /** An IntentFilter that matches the intents that this class can receive. */ + private static final IntentFilter INTENT_FILTER; + static { + IntentFilter filter = new IntentFilter(); + filter.addAction(TestStatusReporter.ACTION_HEARTBEAT); + filter.addAction(TestStatusReporter.ACTION_TEST_FAILED); + filter.addAction(TestStatusReporter.ACTION_TEST_PASSED); + filter.addAction(TestStatusReporter.ACTION_TEST_STARTED); + try { + filter.addDataType(TestStatusReporter.DATA_TYPE_HEARTBEAT); + filter.addDataType(TestStatusReporter.DATA_TYPE_RESULT); + } catch (IntentFilter.MalformedMimeTypeException e) { + Log.wtf(TAG, "Invalid MIME type", e); + } + INTENT_FILTER = filter; + } + + /** A callback used when a test has failed. */ + public interface FailCallback { + void testFailed(String testClass, String testMethod); + } + + /** A callback used when a heartbeat is received. */ + public interface HeartbeatCallback { + void heartbeat(); + } + + /** A callback used when a test has passed. */ + public interface PassCallback { + void testPassed(String testClass, String testMethod); + } + + /** A callback used when a test has started. */ + public interface StartCallback { + void testStarted(String testClass, String testMethod); + } + + /** Register a callback for when a test has failed. */ + public void registerCallback(FailCallback c) { + mFailCallbacks.add(c); + } + + /** Register a callback for when a heartbeat is received. */ + public void registerCallback(HeartbeatCallback c) { + mHeartbeatCallbacks.add(c); + } + + /** Register a callback for when a test has passed. */ + public void registerCallback(PassCallback c) { + mPassCallbacks.add(c); + } + + /** Register a callback for when a test has started. */ + public void registerCallback(StartCallback c) { + mStartCallbacks.add(c); + } + + /** Register this receiver using the provided context. */ + public void register(Context c) { + c.registerReceiver(this, INTENT_FILTER); + } + + /** Receive a broadcast intent. + * + * @param context The Context in which the receiver is running. + * @param intent The intent received. + */ + @Override + public void onReceive(Context context, Intent intent) { + String testClass = intent.getStringExtra(TestStatusReporter.EXTRA_TEST_CLASS); + String testMethod = intent.getStringExtra(TestStatusReporter.EXTRA_TEST_METHOD); + + switch (intent.getAction()) { + case TestStatusReporter.ACTION_TEST_STARTED: + for (StartCallback c : mStartCallbacks) { + c.testStarted(testClass, testMethod); + } + break; + case TestStatusReporter.ACTION_TEST_PASSED: + for (PassCallback c : mPassCallbacks) { + c.testPassed(testClass, testMethod); + } + break; + case TestStatusReporter.ACTION_TEST_FAILED: + for (FailCallback c : mFailCallbacks) { + c.testFailed(testClass, testMethod); + } + break; + case TestStatusReporter.ACTION_HEARTBEAT: + for (HeartbeatCallback c : mHeartbeatCallbacks) { + c.heartbeat(); + } + break; + default: + Log.e(TAG, "Unrecognized intent received: " + intent.toString()); + break; + } + } + +} + diff --git a/engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusReporter.java b/engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusReporter.java new file mode 100644 index 0000000000..6ac7312fad --- /dev/null +++ b/engine/src/flutter/testing/android/reporter/java/src/org/chromium/test/reporter/TestStatusReporter.java @@ -0,0 +1,83 @@ +// Copyright 2015 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. + +package org.chromium.test.reporter; + +import android.content.Context; +import android.content.Intent; + +import org.chromium.base.ThreadUtils; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Broadcasts test status to any listening {@link org.chromium.test.reporter.TestStatusReceiver}. + */ +public class TestStatusReporter { + + public static final String ACTION_HEARTBEAT = + "org.chromium.test.reporter.TestStatusReporter.HEARTBEAT"; + public static final String ACTION_TEST_STARTED = + "org.chromium.test.reporter.TestStatusReporter.TEST_STARTED"; + public static final String ACTION_TEST_PASSED = + "org.chromium.test.reporter.TestStatusReporter.TEST_PASSED"; + public static final String ACTION_TEST_FAILED = + "org.chromium.test.reporter.TestStatusReporter.TEST_FAILED"; + public static final String DATA_TYPE_HEARTBEAT = "org.chromium.test.reporter/heartbeat"; + public static final String DATA_TYPE_RESULT = "org.chromium.test.reporter/result"; + public static final String EXTRA_TEST_CLASS = + "org.chromium.test.reporter.TestStatusReporter.TEST_CLASS"; + public static final String EXTRA_TEST_METHOD = + "org.chromium.test.reporter.TestStatusReporter.TEST_METHOD"; + + public static final int HEARTBEAT_INTERVAL_MS = 5000; + + private final Context mContext; + private final AtomicBoolean mKeepBeating = new AtomicBoolean(false); + + public TestStatusReporter(Context c) { + mContext = c; + } + + public void startHeartbeat() { + mKeepBeating.set(true); + Runnable heartbeat = new Runnable() { + @Override + public void run() { + Intent i = new Intent(ACTION_HEARTBEAT); + i.setType(DATA_TYPE_HEARTBEAT); + mContext.sendBroadcast(i); + if (mKeepBeating.get()) { + ThreadUtils.postOnUiThreadDelayed(this, HEARTBEAT_INTERVAL_MS); + } + } + }; + ThreadUtils.postOnUiThreadDelayed(heartbeat, HEARTBEAT_INTERVAL_MS); + } + + public void testStarted(String testClass, String testMethod) { + sendBroadcast(testClass, testMethod, ACTION_TEST_STARTED); + } + + public void testPassed(String testClass, String testMethod) { + sendBroadcast(testClass, testMethod, ACTION_TEST_PASSED); + } + + public void testFailed(String testClass, String testMethod) { + sendBroadcast(testClass, testMethod, ACTION_TEST_FAILED); + } + + public void stopHeartbeat() { + mKeepBeating.set(false); + } + + private void sendBroadcast(String testClass, String testMethod, String action) { + Intent i = new Intent(action); + i.setType(DATA_TYPE_RESULT); + i.putExtra(EXTRA_TEST_CLASS, testClass); + i.putExtra(EXTRA_TEST_METHOD, testMethod); + mContext.sendBroadcast(i); + } + +} diff --git a/engine/src/flutter/testing/buildbot/OWNERS b/engine/src/flutter/testing/buildbot/OWNERS new file mode 100644 index 0000000000..c768380d6a --- /dev/null +++ b/engine/src/flutter/testing/buildbot/OWNERS @@ -0,0 +1,18 @@ +# This is needed because of * in testing/OWNERS . +# This is reserved for people that are comfortably with buildbot recipes +# and understand the implications of changing these files. +set noparent + +dpranke@chromium.org +jam@chromium.org +jochen@chromium.org +machenbach@chromium.org +maruel@chromium.org +phajdan.jr@chromium.org +sky@chromium.org +thakis@chromium.org + +per-file chromium.perf.json=eakuefner@chromium.org +per-file chromium.perf.json=fmeawad@chromium.org +per-file chromium.perf.json=simonhatch@chromium.org +per-file chromium.perf.json=sullivan@chromium.org diff --git a/engine/src/flutter/testing/buildbot/PRESUBMIT.py b/engine/src/flutter/testing/buildbot/PRESUBMIT.py new file mode 100644 index 0000000000..a74562a656 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/PRESUBMIT.py @@ -0,0 +1,24 @@ +# Copyright (c) 2012 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. + +"""Enforces json format. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details on the presubmit API built into depot_tools. +""" + + +def CommonChecks(input_api, output_api): + args = [input_api.python_executable, 'manage.py', '--check'] + cmd = input_api.Command( + name='manage', cmd=args, kwargs={}, message=output_api.PresubmitError) + return input_api.RunTests([cmd]) + + +def CheckChangeOnUpload(input_api, output_api): + return CommonChecks(input_api, output_api) + + +def CheckChangeOnCommit(input_api, output_api): + return CommonChecks(input_api, output_api) diff --git a/engine/src/flutter/testing/buildbot/chromium.chrome.json b/engine/src/flutter/testing/buildbot/chromium.chrome.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.chrome.json @@ -0,0 +1 @@ +{} diff --git a/engine/src/flutter/testing/buildbot/chromium.chromiumos.json b/engine/src/flutter/testing/buildbot/chromium.chromiumos.json new file mode 100644 index 0000000000..daa9b22642 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.chromiumos.json @@ -0,0 +1,662 @@ +{ + "Linux ChromiumOS GN": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "chromedriver_unittests", + "chromeos_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "ui_chromeos_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, + "Linux ChromiumOS GN (dbg)": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "chromedriver_unittests", + "chromeos_unittests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "ui_chromeos_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, + "Linux ChromiumOS Ozone Tests (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "ash_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chromeos_unittests" + }, + { + "test": "chromevox_tests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 3 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ozone_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "args": [ + "--test-launcher-print-test-stdio=always" + ], + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_chromeos_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ] + }, + "Linux ChromiumOS Tests (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 5 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chromeos_unittests" + }, + { + "test": "chromevox_tests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "args": [ + "--test-launcher-print-test-stdio=always" + ], + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_chromeos_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ] + }, + "Linux ChromiumOS Tests (dbg)(1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chromeos_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 3 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "args": [ + "--test-launcher-print-test-stdio=always" + ], + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_chromeos_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium.fyi.json b/engine/src/flutter/testing/buildbot/chromium.fyi.json new file mode 100644 index 0000000000..f3ce76092d --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.fyi.json @@ -0,0 +1,3512 @@ +{ + "Browser Side Navigation Linux": { + "gtest_tests": [ + { + "args": [ + "--enable-browser-side-navigation", + "--gtest_filter=-CrossSiteRedirectorBrowserTest.VerifyCrossSiteRedirectURL:CrossSiteTransferTest.NoLeakOnCrossSiteCancel:CrossSiteTransferTest.ReplaceEntryCrossProcessThenTransfer:CrossSiteTransferTest.ReplaceEntryCrossProcessTwice:CrossSiteTransferTest.ReplaceEntryInProcessThenTranfers:DevToolsProtocolTest.NavigationPreservesMessages:NavigationControllerBrowserTest.CorrectLengthWithCurrentItemReplacement:NavigationControllerBrowserTest.FrameNavigationEntry_BlankAutoSubframe:NavigationControllerBrowserTest.NavigationTypeClassification_ClientSideRedirect:NavigationControllerBrowserTest.NavigationTypeClassification_ExistingPage:NavigationControllerBrowserTest.NavigationTypeClassification_InPage:NavigationControllerBrowserTest.NavigationTypeClassification_NewAndAutoSubframe:NavigationControllerBrowserTest.NavigationTypeClassification_NewPage:NavigationControllerBrowserTest.PreventSpoofFromSubframeAndReplace:NavigationControllerBrowserTest.StopCausesFailureDespiteJavaScriptURL:RenderViewImplTest.DecideNavigationPolicy:RenderViewImplTest.NavigationStartOverride:RenderViewImplTest.OnNavigationHttpPost:RenderViewImplTest.ReloadWhileSwappedOut:RenderViewImplTest.TestBackForward:SecurityExploitBrowserTest.AttemptDuplicateRenderViewHost:SecurityExploitBrowserTest.AttemptDuplicateRenderWidgetHost:SecurityExploitBrowserTest.InterstitialCommandFromUnderlyingContent:ServiceWorkerBrowserTest.CrossSiteTransfer:ServiceWorkerBrowserTest.ImportsBustMemcache:ServiceWorkerBrowserTest.Reload:ServiceWorkerBrowserTest.ResponseFromHTTPServiceWorkerIsNotMarkedAsSecure:ServiceWorkerBrowserTest.ResponseFromHTTPSServiceWorkerIsMarkedAsSecure:SessionHistoryTest.CrossFrameFormBackForward:SessionHistoryTest.FrameBackForward:SessionHistoryTest.FrameFormBackForward:SessionHistoryTest.LocationChangeInSubframe:SitePerProcessBrowserTest.CrossSiteDidStopLoading:WebContentsImplBrowserTest.ClearNonVisiblePendingOnFail:WebContentsViewAuraTest.ReplaceStateReloadPushState:WebRtcBrowserTest.CallInsideIframe:WebUIMojoTest.EndToEndPing" + ], + "test": "content_browsertests" + }, + { + "args": [ + "--enable-browser-side-navigation", + "--gtest_filter=-NavigationControllerTest.CopyRestoredStateAndNavigate:RenderFrameHostManagerTest.CancelPendingProperlyDeletesOrSwaps:RenderFrameHostManagerTest.PageDoesBackAndReload:RenderViewHostTest.ResetUnloadOnReload:ResourceDispatcherHostTest.TransferNavigationHtml:ResourceDispatcherHostTest.TransferNavigationText:ResourceDispatcherHostTest.TransferNavigationWithProcessCrash:ResourceDispatcherHostTest.TransferNavigationWithTwoRedirects:ResourceDispatcherHostTest.TransferTwoNavigationsHtml:WebContentsImplTest.CrossSiteNavigationBackPreempted:WebContentsImplTest.CrossSiteNotPreemptedDuringBeforeUnload:WebContentsImplTest.NoEarlyStop:WebContentsImplTest.ShowInterstitialCrashRendererThenGoBack:WebContentsImplTest.ShowInterstitialFromBrowserNewNavigationProceed:WebContentsImplTest.ShowInterstitialThenGoBack" + ], + "test": "content_unittests" + } + ] + }, + "Chromium Mac 10.10": { + "gtest_tests": [ + { + "test": "accessibility_unittests" + }, + { + "test": "app_list_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_mac_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "ClangToTLinux tester": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "ClangToTLinuxASan tester": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 5 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 4 + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ] + }, + "ClangToTMac tester": { + "gtest_tests": [ + { + "test": "accessibility_unittests" + }, + { + "test": "angle_unittests" + }, + { + "test": "app_list_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_mac_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "ClangToTMacASan tester": { + "gtest_tests": [ + { + "test": "accessibility_unittests" + }, + { + "test": "angle_unittests" + }, + { + "test": "app_list_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_mac_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "ClangToTWin tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "ClangToTWin(dbg) tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "ClangToTWin(dll) tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "ClangToTWin64 tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "ClangToTWin64(dbg) tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "ClangToTWin64(dll) tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "CrWinAsan tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "CrWinAsan(dll) tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "CrWinClang tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "CrWinClang(dbg) tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "CrWinClang(shared) tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "CrWinClang64 tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "CrWinClang64(dbg) tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "courgette_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "CrWinClang64(dll) tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "CrWinClangLLD tester": { + "gtest_tests": [ + { + "test": "angle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "Linux ARM Cross-Compile": { + "compile_targets": [ + "browser_tests_run" + ], + "gtest_tests": [ + { + "test": "browser_tests" + } + ] + }, + "Linux Trusty": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Linux Trusty (32)": { + "gtest_tests": [ + { + "test": "accessibility_unittests" + }, + { + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Linux Trusty (dbg)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Linux Trusty (dbg)(32)": { + "gtest_tests": [ + { + "test": "accessibility_unittests" + }, + { + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Site Isolation Linux": { + "gtest_tests": [ + { + "args": [ + "--site-per-process", + "--gtest_filter=-AppApiTest.*:BlockedAppApiTest.*:BrowserTest.ClearPendingOnFailUnlessNTP:BrowserTest.OtherRedirectsDontForkProcess:BrowserTest.WindowOpenClose:ChromeAppAPITest.*:ChromeRenderProcessHostTest.*:ChromeRenderProcessHostTestWithCommandLine.*:DevToolsExperimentalExtensionTest.*:DevToolsExtensionTest.*:DnsProbeBrowserTest.*:ErrorPageTest.*:ExecuteScriptApiTest.ExecuteScriptPermissions:ExtensionApiTest.ActiveTab:ExtensionApiTest.ChromeRuntimeOpenOptionsPage:ExtensionApiTest.ContentScriptExtensionIframe:ExtensionApiTest.ContentScriptOtherExtensions:ExtensionApiTest.ContentScriptExtensionProcess:ExtensionApiTest.Tabs2:ExtensionApiTest.TabsOnUpdated:ExtensionApiTest.WindowOpenPopupIframe:ExtensionBrowserTest.LoadChromeExtensionsWithOptionsParamWhenEmbedded:ExtensionCrxInstallerTest.InstallDelayedUntilNextUpdate:ExtensionOptionsApiTest.ExtensionCanEmbedOwnOptions:ExtensionWebUITest.CanEmbedExtensionOptions:ExtensionWebUITest.ReceivesExtensionOptionsOnClose:InlineLoginUISafeIframeBrowserTest.*:IsolatedAppTest.*:LaunchWebAuthFlowFunctionTest.*:MimeHandlerViewTest.*:*.NewAvatarMenuEnabledInGuestMode:OptionsUIBrowserTest.*:*PDFExtensionTest.*:PhishingClassifierTest.*:PhishingDOMFeatureExtractorTest.*:PlatformAppUrlRedirectorBrowserTest.*:PopupBlockerBrowserTest.*:PrerenderBrowserTest.*:ProcessManagementTest.*:RedirectTest.*:ReferrerPolicyTest.*:SSLUITest.*:WebNavigationApiTest.CrossProcessFragment:WebNavigationApiTest.ServerRedirectSingleProcess:WebNavigationApiTest.CrossProcessHistory:WebViewCommonTest.*:WebViewDPITest.*:WebViewPluginTest.*:WebViewTest.*:ZoomControllerBrowserTest.*:*.NavigateFromNTPToOptionsInSameTab:*.ProfilesWithoutPagesNotLaunched:*.TabMove:*.WhitelistedExtension:*.NewTabPageURL:*.HomepageLocation:RestoreOnStartupPolicyTest*:PhishingClassifierDelegateTest.*:WebUIWebViewBrowserTest*:WebRtcBrowserTest.RunsAudioVideoWebRTCCallInTwoTabs" + ], + "test": "browser_tests" + }, + { + "args": [ + "--site-per-process", + "--gtest_filter=-*.AllowTargetedNavigationsAfterSwap:*.DisownOpener:*.SupportCrossProcessPostMessageWithMessagePort" + ], + "test": "content_browsertests" + }, + { + "args": [ + "--site-per-process" + ], + "test": "content_unittests" + }, + { + "args": [ + "--site-per-process" + ], + "test": "unit_tests" + } + ] + }, + "Site Isolation Win": { + "gtest_tests": [ + { + "args": [ + "--site-per-process", + "--gtest_filter=-AppApiTest.*:BlockedAppApiTest.*:BrowserTest.ClearPendingOnFailUnlessNTP:BrowserTest.OtherRedirectsDontForkProcess:BrowserTest.WindowOpenClose:ChromeAppAPITest.*:ChromeRenderProcessHostTest.*:ChromeRenderProcessHostTestWithCommandLine.*:DevToolsExperimentalExtensionTest.*:DevToolsExtensionTest.*:DnsProbeBrowserTest.*:ErrorPageTest.*:ExecuteScriptApiTest.ExecuteScriptPermissions:ExtensionApiTest.ActiveTab:ExtensionApiTest.ChromeRuntimeOpenOptionsPage:ExtensionApiTest.ContentScriptExtensionIframe:ExtensionApiTest.ContentScriptOtherExtensions:ExtensionApiTest.ContentScriptExtensionProcess:ExtensionApiTest.Tabs2:ExtensionApiTest.TabsOnUpdated:ExtensionApiTest.WindowOpenPopupIframe:ExtensionBrowserTest.LoadChromeExtensionsWithOptionsParamWhenEmbedded:ExtensionCrxInstallerTest.InstallDelayedUntilNextUpdate:ExtensionOptionsApiTest.ExtensionCanEmbedOwnOptions:ExtensionWebUITest.CanEmbedExtensionOptions:ExtensionWebUITest.ReceivesExtensionOptionsOnClose:InlineLoginUISafeIframeBrowserTest.*:IsolatedAppTest.*:LaunchWebAuthFlowFunctionTest.*:MimeHandlerViewTest.*:*.NewAvatarMenuEnabledInGuestMode:OptionsUIBrowserTest.*:*PDFExtensionTest.*:PhishingClassifierTest.*:PhishingDOMFeatureExtractorTest.*:PlatformAppUrlRedirectorBrowserTest.*:PopupBlockerBrowserTest.*:PrerenderBrowserTest.*:ProcessManagementTest.*:RedirectTest.*:ReferrerPolicyTest.*:SSLUITest.*:WebNavigationApiTest.CrossProcessFragment:WebNavigationApiTest.ServerRedirectSingleProcess:WebNavigationApiTest.CrossProcessHistory:WebViewCommonTest.*:WebViewDPITest.*:WebViewPluginTest.*:WebViewTest.*:ZoomControllerBrowserTest.*:*.NavigateFromNTPToOptionsInSameTab:*.ProfilesWithoutPagesNotLaunched:*.TabMove:*.WhitelistedExtension:*.NewTabPageURL:*.HomepageLocation:RestoreOnStartupPolicyTest*:PhishingClassifierDelegateTest.*:WebUIWebViewBrowserTest*:WebRtcBrowserTest.RunsAudioVideoWebRTCCallInTwoTabs" + ], + "test": "browser_tests" + }, + { + "args": [ + "--site-per-process", + "--gtest_filter=-*.AllowTargetedNavigationsAfterSwap:*.DisownOpener:*.SupportCrossProcessPostMessageWithMessagePort" + ], + "test": "content_browsertests" + }, + { + "args": [ + "--site-per-process" + ], + "test": "content_unittests" + }, + { + "args": [ + "--site-per-process" + ], + "test": "unit_tests" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium.linux.json b/engine/src/flutter/testing/buildbot/chromium.linux.json new file mode 100644 index 0000000000..a0d832f16b --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.linux.json @@ -0,0 +1,1205 @@ +{ + "Android GN": { + "additional_compile_targets": [ + "chrome_shell_apk", + "mandoline:all" + ] + }, + "Android GN (dbg)": { + "additional_compile_targets": [ + "chrome_shell_apk", + "mandoline:all" + ] + }, + "Android Tests": { + "scripts": [ + { + "name": "webview_licenses", + "script": "webview_licenses.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + } + ] + }, + "Android Tests (dbg)": { + "scripts": [ + { + "name": "webview_licenses", + "script": "webview_licenses.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + } + ] + }, + "Cast Linux": { + "gtest_tests": [ + { + "test": "base_unittests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_base_unittests" + }, + { + "test": "cast_media_unittests" + }, + { + "test": "cast_shell_browser_test" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "url_unittests" + } + ] + }, + "Linux Clang (dbg)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gn_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_application_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "unit_tests" + } + ] + }, + "Linux GN": { + "additional_compile_targets": [ + "browser_tests", + "mandoline:all" + ], + "gtest_tests": [ + { + "test": "accessibility_unittests" + }, + { + "test": "app_list_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_browsertests" + }, + { + "test": "components_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "gn_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "html_viewer_unittests" + }, + { + "test": "interactive_ui_tests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_application_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_runner_unittests" + }, + { + "test": "mojo_shell_unittests" + }, + { + "test": "mojo_surfaces_lib_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "resource_provider_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "view_manager_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "gn_check", + "script": "gn_check.py" + }, + { + "args": [ + "chrome" + ], + "name": "gyp_flag_compare", + "script": "gyp_flag_compare.py" + }, + { + "name": "mojo_apptest", + "script": "mojo_apptest.py" + } + ] + }, + "Linux GN (dbg)": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "mandoline:all", + "media_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "Linux GN Clobber": { + "additional_compile_targets": [ + "all" + ] + }, + "Linux Tests": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 5 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "gn_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "checkdeps", + "script": "checkdeps.py" + }, + { + "name": "checklicenses", + "script": "checklicenses.py" + }, + { + "name": "checkperms", + "script": "checkperms.py" + }, + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Linux Tests (dbg)(1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "gn_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 3 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "args": [ + "--test-launcher-print-test-stdio=always" + ], + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Linux Tests (dbg)(1)(32)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "gn_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium.mac.json b/engine/src/flutter/testing/buildbot/chromium.mac.json new file mode 100644 index 0000000000..4a5ffab86d --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.mac.json @@ -0,0 +1,1064 @@ +{ + "Mac GN": { + "additional_compile_targets": [ + "accessibility_unittests", + "base_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "crypto_unittests", + "gcm_unit_tests", + "gn_unittests", + "gpu_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "url_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, + "Mac GN (dbg)": { + "additional_compile_targets": [ + "accessibility_unittests", + "base_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "crypto_unittests", + "gcm_unit_tests", + "gn_unittests", + "gpu_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "url_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, + "Mac10.10 Tests": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "message_center_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_base_unittests" + }, + { + "test": "url_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Mac10.6 Tests": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "message_center_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_mac_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Mac10.8 Tests": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "message_center_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_mac_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Mac10.9 Tests": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "message_center_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_mac_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Mac10.9 Tests (dbg)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "message_center_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_mac_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium.memory.fyi.json b/engine/src/flutter/testing/buildbot/chromium.memory.fyi.json new file mode 100644 index 0000000000..c577790887 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.memory.fyi.json @@ -0,0 +1,499 @@ +{ + "Linux ChromeOS MSan Tests": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 5 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ] + }, + "Linux MSan Tests": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 5 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ] + }, + "Linux TSan Tests": { + "disabled_tests": { + "app_shell_browsertests": "http://crbug.com/455633", + "browser_tests": "Too many errors, clean content_browsertests first. http://crbug.com/368525", + "cc_unittests": "http://crbug.com/437454", + "interactive_ui_tests": "http://crbug.com/455679" + }, + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 4 + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium.memory.json b/engine/src/flutter/testing/buildbot/chromium.memory.json new file mode 100644 index 0000000000..df3f6ac1ba --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.memory.json @@ -0,0 +1,545 @@ +{ + "Linux ASan LSan Tests (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 3 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 4 + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ] + }, + "Linux ASan Tests (sandboxed)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 5 + }, + "test": "browser_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + } + ] + }, + "Linux Chromium OS ASan LSan Tests (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "chromeos_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 4 + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_chromeos_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + } + ] + }, + "Mac ASan 64 Tests (1)": { + "gtest_tests": [ + { + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 20 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + } + ] + }, + "Mac ASan Tests (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "url_unittests" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium.perf.json b/engine/src/flutter/testing/buildbot/chromium.perf.json new file mode 100644 index 0000000000..565a917200 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.perf.json @@ -0,0 +1,440 @@ +{ + "Android MotoE Perf": { + "scripts": [ + { + "name": "host_info", + "script": "host_info.py" + }, + { + "args": [ + "gpu_perftests" + ], + "name": "gpu_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "cc_perftests" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + } + ] + }, + "Android Nexus10 Perf": { + "scripts": [ + { + "name": "host_info", + "script": "host_info.py" + }, + { + "args": [ + "gpu_perftests" + ], + "name": "gpu_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "cc_perftests" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + } + ] + }, + "Android Nexus4 Perf": { + "scripts": [ + { + "name": "host_info", + "script": "host_info.py" + }, + { + "args": [ + "gpu_perftests" + ], + "name": "gpu_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "cc_perftests" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + } + ] + }, + "Android Nexus5 Perf": { + "scripts": [ + { + "name": "host_info", + "script": "host_info.py" + }, + { + "args": [ + "gpu_perftests" + ], + "name": "gpu_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "cc_perftests" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + } + ] + }, + "Android Nexus6 Perf": { + "scripts": [ + { + "name": "host_info", + "script": "host_info.py" + }, + { + "args": [ + "cc_perftests" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + } + ] + }, + "Android Nexus7v2 Perf": { + "scripts": [ + { + "name": "host_info", + "script": "host_info.py" + }, + { + "args": [ + "gpu_perftests" + ], + "name": "gpu_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "cc_perftests" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + } + ] + }, + "Android Nexus9 Perf": { + "scripts": [ + { + "name": "host_info", + "script": "host_info.py" + }, + { + "args": [ + "gpu_perftests" + ], + "name": "gpu_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "cc_perftests" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + } + ] + }, + "Android One Perf": { + "scripts": [ + { + "name": "host_info", + "script": "host_info.py" + } + ] + }, + "Linux Perf (3)": { + "scripts": [ + { + "args": [ + "cc_perftests", + "--test-launcher-print-test-stdio=always" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Mac 10.8 Perf (1)": { + "scripts": [ + { + "args": [ + "gpu_perftests", + "--test-launcher-print-test-stdio=always" + ], + "name": "gpu_perftests", + "script": "gtest_perf_test.py" + } + ] + }, + "Mac 10.8 Perf (3)": { + "scripts": [ + { + "args": [ + "cc_perftests", + "--test-launcher-print-test-stdio=always" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Mac 10.9 Perf (1)": { + "scripts": [ + { + "args": [ + "gpu_perftests", + "--test-launcher-print-test-stdio=always" + ], + "name": "gpu_perftests", + "script": "gtest_perf_test.py" + } + ] + }, + "Mac 10.9 Perf (3)": { + "scripts": [ + { + "args": [ + "cc_perftests", + "--test-launcher-print-test-stdio=always" + ], + "name": "cc_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Win 7 ATI GPU Perf (2)": { + "scripts": [ + { + "args": [ + "angle_perftests", + "--test-launcher-print-test-stdio=always", + "--test-launcher-jobs=1" + ], + "name": "angle_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Win 7 Intel GPU Perf": { + "scripts": [ + { + "args": [ + "angle_perftests", + "--test-launcher-print-test-stdio=always", + "--test-launcher-jobs=1" + ], + "name": "angle_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Win 7 Low-End Perf (2)": { + "scripts": [ + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Win 7 Nvidia GPU Perf (2)": { + "scripts": [ + { + "args": [ + "angle_perftests", + "--test-launcher-print-test-stdio=always", + "--test-launcher-jobs=1" + ], + "name": "angle_perftests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Win 7 Perf (3)": { + "scripts": [ + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Win 7 x64 Perf (2)": { + "scripts": [ + { + "args": [ + "performance_browser_tests", + "--test-launcher-print-test-stdio=always", + "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*", + "--test-launcher-jobs=1", + "--enable-gpu" + ], + "name": "performance_browser_tests", + "script": "gtest_perf_test.py" + }, + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + }, + "Win 8 Perf (2)": { + "scripts": [ + { + "args": [ + "load_library_perf_tests", + "--test-launcher-print-test-stdio=always" + ], + "name": "load_library_perf_tests", + "script": "gtest_perf_test.py" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium.webkit.json b/engine/src/flutter/testing/buildbot/chromium.webkit.json new file mode 100644 index 0000000000..80221c99a3 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.webkit.json @@ -0,0 +1,1038 @@ +{ + "Linux ChromiumOS Tests (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 5 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chromeos_unittests" + }, + { + "test": "chromevox_tests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ] + }, + "Linux ChromiumOS Tests (2)": { + "gtest_tests": [] + }, + "Linux ChromiumOS Tests (dbg)(1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 20 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "chromeos_unittests" + }, + { + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 3 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "test": "midi_unittests" + }, + { + "test": "nacl_helper_nonsfi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ] + }, + "Linux ChromiumOS Tests (dbg)(2)": { + "gtest_tests": [] + }, + "Linux ChromiumOS Tests (dbg)(3)": { + "gtest_tests": [] + }, + "Linux GN": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "nacl_loader_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, + "Linux GN (dbg)": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, + "WebKit Linux": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Linux (dbg)": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Linux 32": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Linux Leak": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Linux Oilpan": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Linux Oilpan (dbg)": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Linux Oilpan Leak": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Mac Oilpan": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Mac Oilpan (dbg)": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Mac10.6": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Mac10.6 (dbg)": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Mac10.7": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Mac10.7 (dbg)": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Mac10.8": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Mac10.8 (retina)": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Mac10.9": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Win Oilpan": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Win Oilpan (dbg)": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Win7": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit Win7 (dbg)": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + }, + "WebKit XP": { + "gtest_tests": [ + { + "test": "blink_heap_unittests" + }, + { + "test": "blink_platform_unittests" + }, + { + "test": "webkit_unit_tests" + }, + { + "test": "wtf_unittests" + } + ], + "scripts": [ + { + "name": "webkit_lint", + "script": "webkit_lint.py" + }, + { + "name": "webkit_python_tests", + "script": "webkit_python_tests.py" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium.webrtc.fyi.json b/engine/src/flutter/testing/buildbot/chromium.webrtc.fyi.json new file mode 100644 index 0000000000..65ed33a619 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.webrtc.fyi.json @@ -0,0 +1,280 @@ +{ + "Android GN": { + "additional_compile_targets": [ + "chrome_shell_apk" + ] + }, + "Android GN (dbg)": { + "additional_compile_targets": [ + "chrome_shell_apk" + ] + }, + "Linux GN": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "base_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "mandoline:all", + "media_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "Linux GN (dbg)": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "base_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "mandoline:all", + "media_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "Mac GN": { + "additional_compile_targets": [ + "accessibility_unittests", + "base_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "crypto_unittests", + "gcm_unit_tests", + "gn_unittests", + "gpu_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "url_unittests" + ] + }, + "Mac GN (dbg)": { + "additional_compile_targets": [ + "accessibility_unittests", + "base_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "crypto_unittests", + "gcm_unit_tests", + "gn_unittests", + "gpu_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "url_unittests" + ] + }, + "Win GN": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "Win GN (dbg)": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium.win.json b/engine/src/flutter/testing/buildbot/chromium.win.json new file mode 100644 index 0000000000..f1fb87904d --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium.win.json @@ -0,0 +1,1476 @@ +{ + "Vista Tests (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [] + }, + "Win 7 Tests x64 (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "keyboard_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Win x64 GN": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "Win x64 GN (dbg)": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "Win7 Tests (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "keyboard_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "test": "message_center_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + }, + "Win7 Tests (dbg)(1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 20 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 4 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + } + ] + }, + "Win8 Aura": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ], + "gtest_tests": [ + { + "test": "content_browsertests" + }, + { + "test": "events_unittests" + }, + { + "test": "ui_touch_selection_unittests" + } + ] + }, + "Win8 GN": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "Win8 GN (dbg)": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "XP Tests (1)": { + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "test": "app_shell_unittests" + }, + { + "test": "aura_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "test": "chrome_elf_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "test": "compositor_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "test": "events_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "test": "installer_util_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "interactive_ui_tests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "sbox_integration_tests" + }, + { + "test": "sbox_unittests" + }, + { + "test": "sbox_validation_tests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "telemetry_unittests", + "script": "telemetry_unittests.py" + }, + { + "name": "telemetry_perf_unittests", + "script": "telemetry_perf_unittests.py" + }, + { + "name": "nacl_integration", + "script": "nacl_integration.py" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/chromium_arm.json b/engine/src/flutter/testing/buildbot/chromium_arm.json new file mode 100644 index 0000000000..c3d306d08d --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium_arm.json @@ -0,0 +1,14 @@ +{ + "compile_targets": [ + "browser_tests_run", + "nacl_helper_nonsfi_unittests_run", + "nacl_loader_unittests_run", + "sandbox_linux_unittests_run" + ], + "gtest_tests": [ + "browser_tests", + "nacl_helper_nonsfi_unittests", + "nacl_loader_unittests", + "sandbox_linux_unittests" + ] +} diff --git a/engine/src/flutter/testing/buildbot/chromium_memory_trybot.json b/engine/src/flutter/testing/buildbot/chromium_memory_trybot.json new file mode 100644 index 0000000000..cbe9dc9027 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium_memory_trybot.json @@ -0,0 +1,122 @@ +{ + "gtest_tests": [ + "accessibility_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 10 + }, + "test": "browser_tests" + }, + "cacheinvalidation_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + "device_unittests", + { + "platforms": [ + "linux" + ], + "test": "display_unittests" + }, + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 3 + }, + "test": "interactive_ui_tests" + }, + "ipc_tests", + "jingle_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "net_unittests" + }, + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + { + "platforms": [ + "linux" + ], + "test": "sandbox_linux_unittests" + }, + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "unit_tests" + }, + "url_unittests" + ] +} diff --git a/engine/src/flutter/testing/buildbot/chromium_trybot.json b/engine/src/flutter/testing/buildbot/chromium_trybot.json new file mode 100644 index 0000000000..59c8c6fde4 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium_trybot.json @@ -0,0 +1,379 @@ +{ + "compile_targets": [ + "all" + ], + "filter_compile_builders": [ + "win_chromium_compile_dbg", + "win_chromium_rel_swarming", + "win_chromium_x64_rel_swarming", + "win8_chromium_rel" + ], + "gtest_tests": [ + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "accessibility_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "app_list_unittests" + }, + { + "chromium_configs": [ + "chromium_chromeos", + "chromium_chromeos_clang", + "chromium_chromeos_ozone" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "platforms": [ + "win" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ash_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 8 + }, + "test": "browser_tests" + }, + "cacheinvalidation_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cast_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "cc_unittests" + }, + "chromedriver_unittests", + { + "chromium_configs": [ + "chromium_chromeos", + "chromium_chromeos_clang", + "chromium_chromeos_ozone" + ], + "test": "chromevox_tests" + }, + { + "platforms": [ + "win" + ], + "test": "chrome_elf_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "components_unittests" + }, + { + "platforms": [ + "linux", + "win" + ], + "test": "compositor_unittests" + }, + { + "can_use_on_swarming_builders": true, + "platforms": [ + "win" + ], + "test": "courgette_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_browsertests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "content_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "crypto_unittests" + }, + { + "platforms": [ + "linux" + ], + "test": "dbus_unittests" + }, + "device_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "events_unittests" + }, + { + "platforms": [ + "win", + "mac", + "linux" + ], + "test": "extensions_unittests" + }, + "gcm_unit_tests", + { + "chromium_configs": [ + "chromium_chromeos", + "chromium_chromeos_clang", + "chromium_chromeos_ozone" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gfx_unittests" + }, + "google_apis_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "gpu_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 3 + }, + "test": "interactive_ui_tests" + }, + { + "platforms": [ + "win" + ], + "test": "installer_util_unittests" + }, + { + "platforms": [ + "linux" + ], + "test": "ipc_mojo_unittests" + }, + "ipc_tests", + "jingle_unittests", + { + "platforms": [ + "linux", + "win" + ], + "test": "keyboard_unittests" + }, + "media_perftests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "media_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "message_center_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "midi_unittests" + }, + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + { + "platforms": [ + "linux" + ], + "test": "nacl_helper_nonsfi_unittests" + }, + "nacl_loader_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "net_unittests" + }, + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + { + "platforms": [ + "win" + ], + "test": "sbox_integration_tests" + }, + { + "platforms": [ + "win" + ], + "test": "sbox_validation_tests" + }, + { + "platforms": [ + "win" + ], + "test": "sbox_unittests" + }, + "sql_unittests", + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 3 + }, + "test": "sync_integration_tests" + }, + "sync_unit_tests", + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_base_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "ui_touch_selection_unittests" + }, + { + "swarming": { + "can_use_on_swarming_builders": true, + "shards": 2 + }, + "test": "unit_tests" + }, + { + "chromium_configs": [ + "chromium_chromeos", + "chromium_chromeos_clang", + "chromium_chromeos_ozone" + ], + "platforms": [ + "linux" + ], + "test": "ui_chromeos_unittests" + }, + "url_unittests", + "skia_unittests", + { + "platforms": [ + "linux", + "win" + ], + "test": "wm_unittests" + }, + { + "platforms": [ + "linux", + "win" + ], + "swarming": { + "can_use_on_swarming_builders": true + }, + "test": "extensions_browsertests" + }, + { + "platforms": [ + "linux", + "win" + ], + "test": "app_shell_unittests" + }, + { + "platforms": [ + "linux", + "win" + ], + "test": "aura_unittests" + }, + { + "platforms": [ + "linux", + "win" + ], + "test": "views_unittests" + }, + { + "args": [ + "--test-launcher-print-test-stdio=always" + ], + "platforms": [ + "linux" + ], + "test": "sandbox_linux_unittests" + }, + { + "platforms": [ + "mac" + ], + "test": "sandbox_mac_unittests" + }, + { + "chromium_configs": [ + "chromium_chromeos", + "chromium_chromeos_clang", + "chromium_chromeos_ozone" + ], + "platforms": [ + "linux" + ], + "test": "chromeos_unittests" + }, + { + "platforms": [ + "linux" + ], + "test": "display_unittests" + }, + { + "chromium_configs": [ + "chromium_chromeos_ozone" + ], + "platforms": [ + "linux" + ], + "test": "ozone_unittests" + } + ], + "non_filter_builders": [], + "non_filter_tests_builders": [] +} diff --git a/engine/src/flutter/testing/buildbot/chromium_win8_trybot.json b/engine/src/flutter/testing/buildbot/chromium_win8_trybot.json new file mode 100644 index 0000000000..38e2f614e9 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/chromium_win8_trybot.json @@ -0,0 +1,19 @@ +{ + "compile_targets": [ + "all" + ], + "gtest_tests": [ + "base_unittests", + "views_unittests", + "wm_unittests", + "aura_unittests", + "ash_unittests", + "compositor_unittests", + "content_browsertests", + "events_unittests", + "ui_touch_selection_unittests", + "sbox_integration_tests", + "sbox_validation_tests", + "sbox_unittests" + ] +} diff --git a/engine/src/flutter/testing/buildbot/client.v8.branches.json b/engine/src/flutter/testing/buildbot/client.v8.branches.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/engine/src/flutter/testing/buildbot/client.v8.branches.json @@ -0,0 +1 @@ +{} diff --git a/engine/src/flutter/testing/buildbot/client.v8.fyi.json b/engine/src/flutter/testing/buildbot/client.v8.fyi.json new file mode 100644 index 0000000000..2f43da0412 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/client.v8.fyi.json @@ -0,0 +1,64 @@ +{ + "V8 Android GN (dbg)": { + "additional_compile_targets": [ + "chrome_shell_apk" + ], + "gtest_tests": [] + }, + "V8 Linux GN": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "nacl_loader_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ], + "gtest_tests": [] + } +} diff --git a/engine/src/flutter/testing/buildbot/manage.py b/engine/src/flutter/testing/buildbot/manage.py new file mode 100755 index 0000000000..98de7ef4b0 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/manage.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +"""Toolbox to manage all the json files in this directory. + +It can reformat them in their canonical format or ensures they are well +formatted. +""" + +import argparse +import collections +import glob +import json +import os +import subprocess +import sys + + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +SRC_DIR = os.path.dirname(os.path.dirname(THIS_DIR)) +sys.path.insert(0, os.path.join(SRC_DIR, 'third_party', 'colorama', 'src')) + +import colorama + + +SKIP = { + # These are not 'builders'. + 'compile_targets', 'gtest_tests', 'filter_compile_builders', + 'non_filter_builders', 'non_filter_tests_builders', + + # These are not supported on Swarming yet. + # http://crbug.com/472205 + 'Chromium Mac 10.10', + # http://crbug.com/441429 + 'Linux Trusty (32)', 'Linux Trusty (dbg)(32)', + + # http://crbug.com/480053 + 'Linux GN', + 'linux_chromium_gn_rel', + + # Unmaintained builders on chromium.fyi + 'ClangToTMac', + 'ClangToTMacASan', + + # One off builders. Note that Swarming does support ARM. + 'Linux ARM Cross-Compile', + 'Site Isolation Linux', + 'Site Isolation Win', +} + + +class Error(Exception): + """Processing error.""" + + +def get_isolates(): + """Returns the list of all isolate files.""" + files = subprocess.check_output(['git', 'ls-files'], cwd=SRC_DIR).splitlines() + return [os.path.basename(f) for f in files if f.endswith('.isolate')] + + +def process_builder_convert(data, filename, builder, test_name): + """Converts 'test_name' to run on Swarming in 'data'. + + Returns True if 'test_name' was found. + """ + result = False + for test in data['gtest_tests']: + if test['test'] != test_name: + continue + test.setdefault('swarming', {}) + if not test['swarming'].get('can_use_on_swarming_builders'): + print('- %s: %s' % (filename, builder)) + test['swarming']['can_use_on_swarming_builders'] = True + result = True + return result + + +def process_builder_remaining(data, filename, builder, tests_location): + """Calculates tests_location when mode is --remaining.""" + for test in data['gtest_tests']: + name = test['test'] + if test.get('swarming', {}).get('can_use_on_swarming_builders'): + tests_location[name]['count_run_on_swarming'] += 1 + else: + tests_location[name]['count_run_local'] += 1 + tests_location[name]['local_configs'].setdefault( + filename, []).append(builder) + + +def process_file(mode, test_name, tests_location, filepath): + """Processes a file. + + The action depends on mode. Updates tests_location. + + Return False if the process exit code should be 1. + """ + filename = os.path.basename(filepath) + with open(filepath) as f: + content = f.read() + try: + config = json.loads(content) + except ValueError as e: + raise Error('Exception raised while checking %s: %s' % (filepath, e)) + + for builder, data in sorted(config.iteritems()): + if builder in SKIP: + # Oddities. + continue + if not isinstance(data, dict): + raise Error('%s: %s is broken: %s' % (filename, builder, data)) + if 'gtest_tests' not in data: + continue + if not isinstance(data['gtest_tests'], list): + raise Error( + '%s: %s is broken: %s' % (filename, builder, data['gtest_tests'])) + if not all(isinstance(g, dict) for g in data['gtest_tests']): + raise Error( + '%s: %s is broken: %s' % (filename, builder, data['gtest_tests'])) + + config[builder]['gtest_tests'] = sorted( + data['gtest_tests'], key=lambda x: x['test']) + if mode == 'remaining': + process_builder_remaining(data, filename, builder, tests_location) + elif mode == 'convert': + process_builder_convert(data, filename, builder, test_name) + + expected = json.dumps( + config, sort_keys=True, indent=2, separators=(',', ': ')) + '\n' + if content != expected: + if mode in ('convert', 'write'): + with open(filepath, 'wb') as f: + f.write(expected) + if mode == 'write': + print('Updated %s' % filename) + else: + print('%s is not in canonical format' % filename) + print('run `testing/buildbot/manage.py -w` to fix') + return mode != 'check' + return True + + +def print_remaining(test_name,tests_location): + """Prints a visual summary of what tests are yet to be converted to run on + Swarming. + """ + if test_name: + if test_name not in tests_location: + raise Error('Unknown test %s' % test_name) + for config, builders in sorted( + tests_location[test_name]['local_configs'].iteritems()): + print('%s:' % config) + for builder in sorted(builders): + print(' %s' % builder) + return + + isolates = get_isolates() + l = max(map(len, tests_location)) + print('%-*s%sLocal %sSwarming %sMissing isolate' % + (l, 'Test', colorama.Fore.RED, colorama.Fore.GREEN, + colorama.Fore.MAGENTA)) + total_local = 0 + total_swarming = 0 + for name, location in sorted(tests_location.iteritems()): + if not location['count_run_on_swarming']: + c = colorama.Fore.RED + elif location['count_run_local']: + c = colorama.Fore.YELLOW + else: + c = colorama.Fore.GREEN + total_local += location['count_run_local'] + total_swarming += location['count_run_on_swarming'] + missing_isolate = '' + if name + '.isolate' not in isolates: + missing_isolate = colorama.Fore.MAGENTA + '*' + print('%s%-*s %4d %4d %s' % + (c, l, name, location['count_run_local'], + location['count_run_on_swarming'], missing_isolate)) + + total = total_local + total_swarming + p_local = 100. * total_local / total + p_swarming = 100. * total_swarming / total + print('%s%-*s %4d (%4.1f%%) %4d (%4.1f%%)' % + (colorama.Fore.WHITE, l, 'Total:', total_local, p_local, + total_swarming, p_swarming)) + print('%-*s %4d' % (l, 'Total executions:', total)) + + +def main(): + colorama.init() + parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__) + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument( + '-c', '--check', dest='mode', action='store_const', const='check', + default='check', help='Only check the files') + group.add_argument( + '--convert', dest='mode', action='store_const', const='convert', + help='Convert a test to run on Swarming everywhere') + group.add_argument( + '--remaining', dest='mode', action='store_const', const='remaining', + help='Count the number of tests not yet running on Swarming') + group.add_argument( + '-w', '--write', dest='mode', action='store_const', const='write', + help='Rewrite the files') + parser.add_argument( + 'test_name', nargs='?', + help='The test name to print which configs to update; only to be used ' + 'with --remaining') + args = parser.parse_args() + + if args.mode == 'convert': + if not args.test_name: + parser.error('A test name is required with --convert') + if args.test_name + '.isolate' not in get_isolates(): + parser.error('Create %s.isolate first' % args.test_name) + + # Stats when running in --remaining mode; + tests_location = collections.defaultdict( + lambda: { + 'count_run_local': 0, 'count_run_on_swarming': 0, 'local_configs': {} + }) + + try: + result = 0 + for filepath in glob.glob(os.path.join(THIS_DIR, '*.json')): + if not process_file(args.mode, args.test_name, tests_location, filepath): + result = 1 + + if args.mode == 'remaining': + print_remaining(args.test_name, tests_location) + return result + except Error as e: + sys.stderr.write('%s\n' % e) + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/engine/src/flutter/testing/buildbot/trybot_analyze_config.json b/engine/src/flutter/testing/buildbot/trybot_analyze_config.json new file mode 100644 index 0000000000..5608ae4b43 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/trybot_analyze_config.json @@ -0,0 +1,54 @@ +{ + "android_webview": { + "exclusions": [ + "android_webview/.*" + ] + }, + "base": { + "exclusions": [ + ".*isolate", + "build/.*gyp[i]?", + "build/android/.*py", + "build/android/pylib/.*", + "build/compiler_version.py", + "build/get_landmines.py", + "build/gyp_chromium", + "build/linux/sysroot_ld_path.sh", + "DEPS", + "testing/buildbot/.*", + "testing/test_env.py", + "tools/clang/blink_gc_plugin/CMakeLists.txt", + "tools/luci-go/.*", + "tools/whitespace.txt" + ] + }, + "chromium": { + "exclusions": [ + "chrome/test/data/.*", + "chrome/installer/linux/sysroot_scripts/install-debian.wheezy.sysroot.py", + "components/test/data/.*", + "content/test/data/.*", + "content/test/gpu/.*", + "extensions/test/data/.*", + "gpu/gles2_conform_support/gles2_conform_test_expectations.txt", + "mojo/tools/.*", + "media/test/data/.*", + "net/data/.*", + "sql/test/data/.*", + "third_party/accessibility-audit/axs_testing.js", + "third_party/hunspell_dictionaries/.*", + "third_party/zlib/google/test/data/.*", + "tools/clang/scripts/update.py", + "tools/clang/scripts/update.sh", + "tools/mb/.*", + "tools/metrics/histograms/histograms.xml", + "tools/perf/.*", + "tools/telemetry/.*" + ] + }, + "ios": { + "exclusions": [ + "ios/build/bots/.*" + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/tryserver.blink.json b/engine/src/flutter/testing/buildbot/tryserver.blink.json new file mode 100644 index 0000000000..303f773a17 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/tryserver.blink.json @@ -0,0 +1,64 @@ +{ + "android_chromium_gn_compile_rel": { + "additional_compile_targets": [ + "chrome_shell_apk" + ], + "gtest_tests": [] + }, + "linux_chromium_gn_rel": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/tryserver.chromium.linux.json b/engine/src/flutter/testing/buildbot/tryserver.chromium.linux.json new file mode 100644 index 0000000000..876d8a50f4 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/tryserver.chromium.linux.json @@ -0,0 +1,357 @@ +{ + "android_chromium_gn_compile_dbg": { + "additional_compile_targets": [ + "chrome_shell_apk", + "mandoline:all" + ] + }, + "android_chromium_gn_compile_rel": { + "additional_compile_targets": [ + "chrome_shell_apk", + "mandoline:all" + ] + }, + "linux_chromium_gn_chromeos_dbg": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "chromedriver_unittests", + "chromeos_unittests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_tests", + "jingle_unittests", + "mandoline:all", + "media_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "ui_chromeos_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "linux_chromium_gn_chromeos_rel": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "chromedriver_unittests", + "chromeos_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_tests", + "jingle_unittests", + "mandoline:all", + "media_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "ui_chromeos_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "linux_chromium_gn_dbg": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "mandoline:all", + "media_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "linux_chromium_gn_rel": { + "additional_compile_targets": [ + "browser_tests", + "mandoline:all" + ], + "gtest_tests": [ + { + "test": "accessibility_unittests" + }, + { + "test": "app_list_unittests" + }, + { + "test": "aura_unittests" + }, + { + "test": "base_unittests" + }, + { + "test": "cacheinvalidation_unittests" + }, + { + "test": "cast_unittests" + }, + { + "test": "cc_unittests" + }, + { + "test": "chromedriver_unittests" + }, + { + "test": "components_browsertests" + }, + { + "test": "components_unittests" + }, + { + "test": "content_browsertests" + }, + { + "test": "content_unittests" + }, + { + "test": "crypto_unittests" + }, + { + "test": "dbus_unittests" + }, + { + "test": "device_unittests" + }, + { + "test": "display_unittests" + }, + { + "test": "events_unittests" + }, + { + "test": "extensions_browsertests" + }, + { + "test": "extensions_unittests" + }, + { + "test": "gcm_unit_tests" + }, + { + "test": "gfx_unittests" + }, + { + "test": "gl_unittests" + }, + { + "test": "gn_unittests" + }, + { + "test": "google_apis_unittests" + }, + { + "test": "gpu_unittests" + }, + { + "test": "html_viewer_unittests" + }, + { + "test": "interactive_ui_tests" + }, + { + "test": "ipc_mojo_unittests" + }, + { + "test": "ipc_tests" + }, + { + "test": "jingle_unittests" + }, + { + "test": "media_unittests" + }, + { + "test": "mojo_common_unittests" + }, + { + "test": "mojo_public_application_unittests" + }, + { + "test": "mojo_public_bindings_unittests" + }, + { + "test": "mojo_public_environment_unittests" + }, + { + "test": "mojo_public_system_unittests" + }, + { + "test": "mojo_public_utility_unittests" + }, + { + "test": "mojo_runner_unittests" + }, + { + "test": "mojo_shell_unittests" + }, + { + "test": "mojo_surfaces_lib_unittests" + }, + { + "test": "mojo_system_unittests" + }, + { + "test": "nacl_loader_unittests" + }, + { + "test": "net_unittests" + }, + { + "test": "ppapi_unittests" + }, + { + "test": "printing_unittests" + }, + { + "test": "remoting_unittests" + }, + { + "test": "resource_provider_unittests" + }, + { + "test": "sandbox_linux_unittests" + }, + { + "test": "skia_unittests" + }, + { + "test": "sql_unittests" + }, + { + "test": "sync_integration_tests" + }, + { + "test": "sync_unit_tests" + }, + { + "test": "ui_base_unittests" + }, + { + "test": "ui_touch_selection_unittests" + }, + { + "test": "unit_tests" + }, + { + "test": "url_unittests" + }, + { + "test": "view_manager_unittests" + }, + { + "test": "views_unittests" + }, + { + "test": "wm_unittests" + } + ], + "scripts": [ + { + "name": "gn_check", + "script": "gn_check.py" + }, + { + "args": [ + "chrome" + ], + "name": "gyp_flag_compare", + "script": "gyp_flag_compare.py" + }, + { + "name": "mojo_apptest", + "script": "mojo_apptest.py" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/tryserver.chromium.mac.json b/engine/src/flutter/testing/buildbot/tryserver.chromium.mac.json new file mode 100644 index 0000000000..4a46f9a42d --- /dev/null +++ b/engine/src/flutter/testing/buildbot/tryserver.chromium.mac.json @@ -0,0 +1,72 @@ +{ + "mac_chromium_gn_dbg": { + "additional_compile_targets": [ + "accessibility_unittests", + "base_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "crypto_unittests", + "gcm_unit_tests", + "gn_unittests", + "gpu_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "url_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + }, + "mac_chromium_gn_rel": { + "additional_compile_targets": [ + "accessibility_unittests", + "base_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "crypto_unittests", + "gcm_unit_tests", + "gn_unittests", + "gpu_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "ppapi_unittests", + "printing_unittests", + "skia_unittests", + "sql_unittests", + "sync_unit_tests", + "ui_base_unittests", + "url_unittests" + ], + "gtest_tests": [ + { + "test": "base_unittests" + } + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/tryserver.chromium.perf.json b/engine/src/flutter/testing/buildbot/tryserver.chromium.perf.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/engine/src/flutter/testing/buildbot/tryserver.chromium.perf.json @@ -0,0 +1 @@ +{} diff --git a/engine/src/flutter/testing/buildbot/tryserver.chromium.win.json b/engine/src/flutter/testing/buildbot/tryserver.chromium.win.json new file mode 100644 index 0000000000..41423872d2 --- /dev/null +++ b/engine/src/flutter/testing/buildbot/tryserver.chromium.win.json @@ -0,0 +1,214 @@ +{ + "win8_chromium_gn_dbg": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "win8_chromium_gn_rel": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "win_chromium_gn_x64_dbg": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + }, + "win_chromium_gn_x64_rel": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "app_shell_unittests", + "ash_unittests", + "aura_unittests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chrome", + "chrome_elf_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "compositor_unittests", + "content_browsertests", + "content_unittests", + "courgette_unittests", + "crypto_unittests", + "device_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "keyboard_unittests", + "mandoline:all", + "media_unittests", + "message_center_unittests", + "ppapi_unittests", + "printing_unittests", + "sbox_integration_tests", + "sbox_unittests", + "sbox_validation_tests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "url_unittests", + "views_unittests", + "wm_unittests" + ] + } +} diff --git a/engine/src/flutter/testing/buildbot/tryserver.v8.json b/engine/src/flutter/testing/buildbot/tryserver.v8.json new file mode 100644 index 0000000000..a0f9adf9fa --- /dev/null +++ b/engine/src/flutter/testing/buildbot/tryserver.v8.json @@ -0,0 +1,65 @@ +{ + "v8_android_chromium_gn_dbg": { + "additional_compile_targets": [ + "chrome_shell_apk" + ], + "gtest_tests": [] + }, + "v8_linux_chromium_gn_rel": { + "additional_compile_targets": [ + "accessibility_unittests", + "app_list_unittests", + "aura_unittests", + "browser_tests", + "cacheinvalidation_unittests", + "cast_unittests", + "cc_unittests", + "chromedriver_unittests", + "components_browsertests", + "components_unittests", + "content_browsertests", + "content_unittests", + "crypto_unittests", + "dbus_unittests", + "device_unittests", + "display_unittests", + "events_unittests", + "extensions_browsertests", + "extensions_unittests", + "gcm_unit_tests", + "gfx_unittests", + "gl_unittests", + "gn_unittests", + "google_apis_unittests", + "gpu_unittests", + "interactive_ui_tests", + "ipc_mojo_unittests", + "ipc_tests", + "jingle_unittests", + "media_unittests", + "mojo_common_unittests", + "mojo_public_bindings_unittests", + "mojo_public_environment_unittests", + "mojo_public_system_unittests", + "mojo_public_utility_unittests", + "mojo_system_unittests", + "nacl_loader_unittests", + "net_unittests", + "ppapi_unittests", + "printing_unittests", + "remoting_unittests", + "sandbox_linux_unittests", + "skia_unittests", + "sql_unittests", + "sync_integration_tests", + "sync_unit_tests", + "ui_base_unittests", + "ui_touch_selection_unittests", + "unit_tests", + "url_unittests", + "views_unittests", + "wm_unittests" + ], + "gtest_tests": [] + } +} diff --git a/engine/src/flutter/testing/chromoting/browser_test_commands_linux.txt b/engine/src/flutter/testing/chromoting/browser_test_commands_linux.txt new file mode 100644 index 0000000000..dbc386c18b --- /dev/null +++ b/engine/src/flutter/testing/chromoting/browser_test_commands_linux.txt @@ -0,0 +1,17 @@ +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch:RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gafyd +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=non-gmail +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch:RemoteDesktopBrowserTest.MANUAL_Auth --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_Connect_Local_Host --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_Connect_Local_Host --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_RetryOnHostOffline --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=Me2MeBrowserTest.MANUAL_Me2Me_v2_Alive_OnLostFocus --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_Connect --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_InvalidAccessCode --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_CancelShare --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=It2MeBrowserTest.MANUAL_VerifyAccessCodeNonReusable --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Cancel_PIN --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Update_PIN --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=RemoteDesktopBrowserTest.MANUAL_Invalid_PIN --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123457 --override-user-data-dir=/tmp/chromoting_test_profile +/usr/bin/python ../xvfb.py #PROD_DIR# #PROD_DIR#/browser_tests --gtest_filter=FullscreenBrowserTest.MANUAL_Me2Me_Bump_Scroll --run-manual --ui-test-action-timeout=100000 --webapp-unpacked=#PROD_DIR#/remoting/remoting.webapp.v2 --extension-name=Chromoting --accounts-file=../../remoting/tools/internal/test_accounts.json --account-type=gmail --me2me-pin=123456 --override-user-data-dir=/tmp/chromoting_test_profile +cd ../../remoting/internal/config && /usr/bin/python ./is_valid_json.py diff --git a/engine/src/flutter/testing/chromoting/browser_tests_launcher.py b/engine/src/flutter/testing/chromoting/browser_tests_launcher.py new file mode 100644 index 0000000000..d39d2910e3 --- /dev/null +++ b/engine/src/flutter/testing/chromoting/browser_tests_launcher.py @@ -0,0 +1,271 @@ +# Copyright (c) 2014 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. + + +"""Utility script to launch browser-tests on the Chromoting bot.""" +import argparse +import glob +import hashlib +import os +from os.path import expanduser +import shutil +import socket +import subprocess + +import psutil + +BROWSER_TEST_ID = 'browser_tests' +PROD_DIR_ID = '#PROD_DIR#' +HOST_HASH_VALUE = hashlib.md5(socket.gethostname()).hexdigest() +SUCCESS_INDICATOR = 'SUCCESS: all tests passed.' +NATIVE_MESSAGING_DIR = 'NativeMessagingHosts' +CRD_ID = 'chrome-remote-desktop' # Used in a few file/folder names +CHROMOTING_HOST_PATH = '/opt/google/chrome-remote-desktop/chrome-remote-desktop' +TEST_FAILURE = False +FAILING_TESTS = '' +HOST_READY_INDICATOR = 'Host ready to receive connections.' +BROWSER_NOT_STARTED_ERROR = ( + 'Still waiting for the following processes to finish') +TIME_OUT_INDICATOR = '(TIMED OUT)' +MAX_RETRIES = 1 + + +def LaunchBTCommand(args, command): + """Launches the specified browser-test command. + + If the execution failed because a browser-instance was not launched, retry + once. + Args: + args: Command line args, used for test-case startup tasks. + command: Browser-test command line. + """ + global TEST_FAILURE, FAILING_TESTS + + retries = 0 + while retries <= MAX_RETRIES: + TestCaseSetup(args) + results = RunCommandInSubProcess(command) + + if SUCCESS_INDICATOR in results: + # Test passed. + break + + # Sometimes, during execution of browser-tests, a browser instance is + # not started and the test times out. See http://crbug/480025. + # To work around it, check if this execution failed owing to that + # problem and retry. + # There are 2 things to look for in the results: + # A line saying "Still waiting for the following processes to finish", + # and, because sometimes that line gets logged even if the test + # eventually passes, we'll also look for "(TIMED OUT)", before retrying. + if not ( + BROWSER_NOT_STARTED_ERROR in results and TIME_OUT_INDICATOR in results): + # Test failed for some other reason. Let's not retry. + break + retries += 1 + + # Check that the test passed. + if SUCCESS_INDICATOR not in results: + TEST_FAILURE = True + # Add this command-line to list of tests that failed. + FAILING_TESTS += command + + +def RunCommandInSubProcess(command): + """Creates a subprocess with command-line that is passed in. + + Args: + command: The text of command to be executed. + Returns: + results: stdout contents of executing the command. + """ + + cmd_line = [command] + try: + results = subprocess.check_output(cmd_line, stderr=subprocess.STDOUT, + shell=True) + except subprocess.CalledProcessError, e: + results = e.output + finally: + print results + return results + + +def TestMachineCleanup(user_profile_dir): + """Cleans up test machine so as not to impact other tests. + + Args: + user_profile_dir: the user-profile folder used by Chromoting tests. + + """ + # Stop the host service. + RunCommandInSubProcess(CHROMOTING_HOST_PATH + ' --stop') + + # Cleanup any host logs. + RunCommandInSubProcess('rm /tmp/chrome_remote_desktop_*') + + # Remove the user-profile dir + if os.path.exists(user_profile_dir): + shutil.rmtree(user_profile_dir) + + +def InitialiseTestMachineForLinux(cfg_file): + """Sets up a Linux machine for connect-to-host browser-tests. + + Copy over me2me host-config to expected locations. + By default, the Linux me2me host expects the host-config file to be under + $HOME/.config/chrome-remote-desktop + Its name is expected to have a hash that is specific to a machine. + + Args: + cfg_file: location of test account's host-config file. + + Raises: + Exception: if host did not start properly. + """ + + # First get home directory on current machine. + home_dir = expanduser('~') + default_config_file_location = os.path.join(home_dir, '.config', CRD_ID) + if os.path.exists(default_config_file_location): + shutil.rmtree(default_config_file_location) + os.makedirs(default_config_file_location) + + # Copy over test host-config to expected location, with expected file-name. + # The file-name should contain a hash-value that is machine-specific. + default_config_file_name = 'host#%s.json' % HOST_HASH_VALUE + config_file_src = os.path.join(os.getcwd(), cfg_file) + shutil.copyfile( + config_file_src, + os.path.join(default_config_file_location, default_config_file_name)) + + # Make sure chromoting host is running. + if not RestartMe2MeHost(): + # Host start failed. Don't run any tests. + raise Exception('Host restart failed.') + + +def RestartMe2MeHost(): + """Stops and starts the Me2Me host on the test machine. + + Waits to confirm that host is ready to receive connections before returning. + + Returns: + True: if HOST_READY_INDICATOR is found in stdout, indicating host is ready. + False: if HOST_READY_INDICATOR not found in stdout. + """ + + # Stop chromoting host. + RunCommandInSubProcess(CHROMOTING_HOST_PATH + ' --stop') + # Start chromoting host. + results = RunCommandInSubProcess(CHROMOTING_HOST_PATH + ' --start') + # Confirm that the start process completed, and we got: + # "Host ready to receive connections." in the log. + if HOST_READY_INDICATOR not in results: + return False + return True + + +def SetupUserProfileDir(me2me_manifest_file, it2me_manifest_file, + user_profile_dir): + """Sets up the Google Chrome user profile directory. + + Delete the previous user profile directory if exists and create a new one. + This invalidates any state changes by the previous test so each test can start + with the same environment. + + When a user launches the remoting web-app, the native messaging host process + is started. For this to work, this function places the me2me and it2me native + messaging host manifest files in a specific folder under the user-profile dir. + + Args: + me2me_manifest_file: location of me2me native messaging host manifest file. + it2me_manifest_file: location of it2me native messaging host manifest file. + user_profile_dir: Chrome user-profile-directory. + """ + native_messaging_folder = os.path.join(user_profile_dir, NATIVE_MESSAGING_DIR) + + if os.path.exists(user_profile_dir): + shutil.rmtree(user_profile_dir) + os.makedirs(native_messaging_folder) + + manifest_files = [me2me_manifest_file, it2me_manifest_file] + for manifest_file in manifest_files: + manifest_file_src = os.path.join(os.getcwd(), manifest_file) + manifest_file_dest = ( + os.path.join(native_messaging_folder, os.path.basename(manifest_file))) + shutil.copyfile(manifest_file_src, manifest_file_dest) + + +def PrintRunningProcesses(): + processes = psutil.get_process_list() + processes = sorted(processes, key=lambda process: process.name) + + print 'List of running processes:\n' + for process in processes: + print process.name + + +def TestCaseSetup(args): + # Stop+start me2me host process. + if not RestartMe2MeHost(): + # Host restart failed. Don't run any more tests. + raise Exception('Host restart failed.') + + # Reset the user profile directory to start each test with a clean slate. + SetupUserProfileDir(args.me2me_manifest_file, args.it2me_manifest_file, + args.user_profile_dir) + + +def main(args): + + InitialiseTestMachineForLinux(args.cfg_file) + + with open(args.commands_file) as f: + for line in f: + # Replace the PROD_DIR value in the command-line with + # the passed in value. + line = line.replace(PROD_DIR_ID, args.prod_dir) + # Launch specified command line for test. + LaunchBTCommand(args, line) + + # All tests completed. Include host-logs in the test results. + host_log_contents = '' + # There should be only 1 log file, as we delete logs on test completion. + # Loop through matching files, just in case there are more. + for log_file in glob.glob('/tmp/chrome_remote_desktop_*'): + with open(log_file, 'r') as log: + host_log_contents += '\nHOST LOG %s\n CONTENTS:\n%s' % ( + log_file, log.read()) + print host_log_contents + + # Was there any test failure? + if TEST_FAILURE: + print '++++++++++AT LEAST 1 TEST FAILED++++++++++' + print FAILING_TESTS.rstrip('\n') + print '++++++++++++++++++++++++++++++++++++++++++' + raise Exception('At least one test failed.') + +if __name__ == '__main__': + + parser = argparse.ArgumentParser() + parser.add_argument('-f', '--commands_file', + help='path to file listing commands to be launched.') + parser.add_argument('-p', '--prod_dir', + help='path to folder having product and test binaries.') + parser.add_argument('-c', '--cfg_file', + help='path to test host config file.') + parser.add_argument('--me2me_manifest_file', + help='path to me2me host manifest file.') + parser.add_argument('--it2me_manifest_file', + help='path to it2me host manifest file.') + parser.add_argument( + '-u', '--user_profile_dir', + help='path to user-profile-dir, used by connect-to-host tests.') + command_line_args = parser.parse_args() + try: + main(command_line_args) + finally: + # Stop host and cleanup user-profile-dir. + TestMachineCleanup(command_line_args.user_profile_dir) diff --git a/engine/src/flutter/testing/chromoting/chromoting_integration_tests.isolate b/engine/src/flutter/testing/chromoting/chromoting_integration_tests.isolate new file mode 100644 index 0000000000..eb7fec0544 --- /dev/null +++ b/engine/src/flutter/testing/chromoting/chromoting_integration_tests.isolate @@ -0,0 +1,127 @@ +# Copyright (c) 2014 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. +{ + 'conditions': [ + ['OS=="linux"', { + 'variables': { + 'command': [ + './browser_tests_launcher.py', + '--commands_file', + './browser_test_commands_linux.txt', + '--prod_dir', + '<(PRODUCT_DIR)', + '--cfg_file', + '../../remoting/tools/internal/test-account-host-config.json', + '--me2me_manifest_file', + '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_desktop.json', + '--it2me_manifest_file', + '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_assistance.json', + '--user_profile_dir', + '/tmp/chromoting_test_profile', + ], + 'files': [ + '../xvfb.py', + './browser_tests_launcher.py', + './browser_test_commands_linux.txt', + '<(PRODUCT_DIR)/libosmesa.so', + '<(PRODUCT_DIR)/nacl_irt_x86_64.nexe', + '../../remoting/tools/internal/test-account-host-config.json', + '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_desktop.json', + '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_assistance.json', + '<(PRODUCT_DIR)/remoting-me2me-host.deb', + '<(PRODUCT_DIR)/nacl_helper', + '<(PRODUCT_DIR)/nacl_helper_bootstrap', + '<(PRODUCT_DIR)/pnacl/', + '../../remoting/internal/config/', + ], + }, + }], + ['OS=="linux" and use_ozone==0', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/xdisplaycheck<(EXECUTABLE_SUFFIX)', + ], + }, + }], + ['OS=="linux" or OS=="mac" or OS=="win"', { + 'variables': { + 'files': [ + '../test_env.py', + '<(PRODUCT_DIR)/browser_tests<(EXECUTABLE_SUFFIX)', + '<(PRODUCT_DIR)/remoting/remoting.webapp/', + '<(PRODUCT_DIR)/remoting/remoting.webapp.v2/', + '<(PRODUCT_DIR)/remoting/browser_test_resources/', + '<(PRODUCT_DIR)/resources.pak', + '../../remoting/tools/internal/test_accounts.json', + ], + 'read_only': 1, + }, + }], + ['OS=="linux" or OS=="win"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/chrome_100_percent.pak', + '<(PRODUCT_DIR)/locales/en-US.pak', + ], + }, + }], + ['OS=="win"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/chrome_elf.dll', + '<(PRODUCT_DIR)/libglesv2.dll', + ], + }, + }], + ['OS=="mac"', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/<(mac_product_name).app/', + '<(PRODUCT_DIR)/exif.so', + ], + }, + }], + ['OS=="win"', { + 'variables': { + 'command': [ + '../test_env.py', + '<(PRODUCT_DIR)/browser_tests<(EXECUTABLE_SUFFIX)', + '--gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch:RemoteDesktopBrowserTest.MANUAL_Auth', + '--run-manual', + '--ui-test-action-timeout=100000', + '--webapp-unpacked=<(PRODUCT_DIR)/remoting/remoting.webapp.v2', + '--extension-name=Chromoting', + '--accounts-file=../../remoting/tools/internal/test_accounts.json', + '--account-type=gmail', + ], + }, + }], + ['OS=="mac"', { + 'variables': { + 'command': [ + '../test_env.py', + '<(PRODUCT_DIR)/browser_tests<(EXECUTABLE_SUFFIX)', + '--gtest_filter=RemoteDesktopBrowserTest.MANUAL_Launch', + '--run-manual', + '--ui-test-action-timeout=100000', + '--webapp-unpacked=<(PRODUCT_DIR)/remoting/remoting.webapp.v2', + '--extension-name=Chromoting', + '--accounts-file=../../remoting/tools/internal/test_accounts.json', + '--account-type=gmail', + ], + }, + }], + ['OS=="win" and kasko==1', { + 'variables': { + 'files': [ + '<(PRODUCT_DIR)/kasko.dll', + ], + }, + }], + ], + 'includes': [ + '../../base/base.isolate', + '../../gin/v8.isolate', + ], +} diff --git a/engine/src/flutter/testing/chromoting/integration_tests.gyp b/engine/src/flutter/testing/chromoting/integration_tests.gyp new file mode 100644 index 0000000000..c3f7912b48 --- /dev/null +++ b/engine/src/flutter/testing/chromoting/integration_tests.gyp @@ -0,0 +1,31 @@ +# Copyright (c) 2014 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. + +{ + 'conditions': [ + ['archive_chromoting_tests==1', { + 'targets': [ + { + 'target_name': 'chromoting_integration_tests_run', + 'includes': [ + './dependencies.gypi', + ], + 'sources': [ + 'chromoting_integration_tests.isolate', + ], + }, # target_name: 'chromoting_integration_tests_run' + { + 'target_name': 'chromoting_multi_machine_example_test', + 'includes': [ + './dependencies.gypi', + ], + 'sources': [ + 'multi_machine_example/example_test_controller.isolate', + 'multi_machine_example/example_task.isolate', + ], + }, # target_name: 'chromoting_multi_machine_example_test' + ], + }], + ], +} diff --git a/engine/src/flutter/testing/chromoting/multi_machine_example/example_task.isolate b/engine/src/flutter/testing/chromoting/multi_machine_example/example_task.isolate new file mode 100644 index 0000000000..710e6da5ed --- /dev/null +++ b/engine/src/flutter/testing/chromoting/multi_machine_example/example_task.isolate @@ -0,0 +1,18 @@ +# Copyright 2015 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. + +{ + 'includes': [ + '../chromoting_integration_tests.isolate', + '../../legion/legion.isolate', + '../../../chrome/chrome.isolate', + '../../../remoting/tools/remote_test_helper/remote_test_helper.isolate', + ], + 'variables': { + 'command': [ + 'python', + '../../legion/run_task.py', + ], + }, +} diff --git a/engine/src/flutter/testing/chromoting/multi_machine_example/example_test_controller.isolate b/engine/src/flutter/testing/chromoting/multi_machine_example/example_test_controller.isolate new file mode 100644 index 0000000000..957cae4246 --- /dev/null +++ b/engine/src/flutter/testing/chromoting/multi_machine_example/example_test_controller.isolate @@ -0,0 +1,31 @@ +# Copyright 2015 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. + +{ + 'includes': [ + '../../legion/legion.isolate', + ], + 'variables': { + 'command': [ + 'python', + 'example_test_controller.py', + '--commands_file', + '../browser_test_commands_linux.txt', + '--prod_dir', + '<(PRODUCT_DIR)', + '--cfg_file', + '../../../remoting/tools/internal/test-account-host-config.json', + '--me2me_manifest_file', + '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_desktop.json', + '--it2me_manifest_file', + '<(PRODUCT_DIR)/remoting/com.google.chrome.remote_assistance.json', + '--user_profile_dir', + '/tmp/chromoting_test_profile', + ], + 'files': [ + 'example_test_controller.py', + '../../../tools/swarming_client/', + ], + }, +} diff --git a/engine/src/flutter/testing/chromoting/multi_machine_example/example_test_controller.py b/engine/src/flutter/testing/chromoting/multi_machine_example/example_test_controller.py new file mode 100755 index 0000000000..2ba2024c1c --- /dev/null +++ b/engine/src/flutter/testing/chromoting/multi_machine_example/example_test_controller.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +"""The test controller for the chromoting localhost browser_tests. + +This test uses the legion framework to setup this controller which will run +the chromoting_integration_tests on a task machine. This is intended to be an +example Legion-based test for the chromoting team. + +The controller will start a task machine to run browser_tests_launcher on. The +output of these tests are streamed back to the test controller to be output +on the controller's stdout and stderr channels. The final test output is then +read and becomes the final output of the controller, mirroring the test's +pass/fail result. +""" + +import argparse +import logging +import os +import sys +import time + +# Map the legion directory so we can import the host controller. +SRC_DIR = os.path.join('..', '..', '..') +sys.path.append(os.path.join(SRC_DIR, 'testing')) +from legion import test_controller + + +class ExampleController(test_controller.TestController): + """The test controller for the Chromoting browser_tests.""" + + def __init__(self): + super(ExampleController, self).__init__() + self.task = None + self.args = None + + def RunTest(self): + """Main method to run the test code.""" + self.ParseArgs() + self.CreateTask() + self.TestIntegrationTests() + + def CreateBrowserTestsLauncherCommand(self): + return [ + 'python', + self.TaskAbsPath('../browser_tests_launcher.py'), + '--commands_file', self.TaskAbsPath(self.args.commands_file), + '--prod_dir', self.TaskAbsPath(self.args.prod_dir), + '--cfg_file', self.TaskAbsPath(self.args.cfg_file), + '--me2me_manifest_file', self.TaskAbsPath( + self.args.me2me_manifest_file), + '--it2me_manifest_file', self.TaskAbsPath( + self.args.it2me_manifest_file), + '--user_profile_dir', self.args.user_profile_dir, + ] + + def TaskAbsPath(self, path): + """Returns the absolute path to the resource on the task machine. + + Args: + path: The relative path to the resource. + + Since the test controller and the task machines run in different tmp dirs + on different machines the absolute path cannot be calculated correctly on + this machine. This function maps the relative path (from this directory) + to an absolute path on the task machine. + """ + return self.task.rpc.AbsPath(path) + + def CreateTask(self): + """Creates a task object and sets the proper values.""" + self.task = self.CreateNewTask( + isolated_hash=self.args.task_machine, + dimensions={'os': 'Ubuntu-14.04', 'pool': 'Chromoting'}) + self.task.Create() + self.task.WaitForConnection() + + def ParseArgs(self): + """Gets the command line args.""" + parser = argparse.ArgumentParser() + parser.add_argument('--task_machine', + help='isolated hash of the task machine.') + # The rest of the args are taken from + # testing/chromoting/browser_tests_launcher.py. + parser.add_argument('-f', '--commands_file', + help='path to file listing commands to be launched.') + parser.add_argument('-p', '--prod_dir', + help='path to folder having product and test binaries.') + parser.add_argument('-c', '--cfg_file', + help='path to test host config file.') + parser.add_argument('--me2me_manifest_file', + help='path to me2me host manifest file.') + parser.add_argument('--it2me_manifest_file', + help='path to it2me host manifest file.') + parser.add_argument( + '-u', '--user_profile_dir', + help='path to user-profile-dir, used by connect-to-host tests.') + self.args, _ = parser.parse_known_args() + + def TestIntegrationTests(self): + """Runs the integration tests via browser_tests_launcher.py.""" + # Create a process object, configure it, and start it. + # All interactions with the process are based on this "proc" key. + proc = self.task.rpc.subprocess.Process( + self.CreateBrowserTestsLauncherCommand()) + # Set the cwd to browser_tests_launcher relative to this directory. + # This allows browser_test_launcher to use relative paths. + self.task.rpc.subprocess.SetCwd(proc, '../') + # Set the task verbosity to true to allow stdout/stderr to be echo'ed to + # run_task's stdout/stderr on the task machine. This can assist in + # debugging. + self.task.rpc.subprocess.SetVerbose(proc) + # Set the process as detached to create it in a new process group. + self.task.rpc.subprocess.SetDetached(proc) + # Start the actual process on the task machine. + self.task.rpc.subprocess.Start(proc) + + # Collect the stdout/stderr and emit it from this controller while the + # process is running. + while self.task.rpc.subprocess.Poll(proc) is None: + # Output the test's stdout and stderr in semi-realtime. + # This is not true realtime due to the RPC calls and the 1s sleep. + stdout, stderr = self.task.rpc.subprocess.ReadOutput(proc) + if stdout: + sys.stdout.write(stdout) + if stderr: + sys.stderr.write(stderr) + time.sleep(1) + + # Get the return code, clean up the process object. + returncode = self.task.rpc.subprocess.GetReturncode(proc) + self.task.rpc.subprocess.Delete(proc) + + # Pass or fail depending on the return code from the browser_tests_launcher. + if returncode != 0: + raise AssertionError('browser_tests_launcher failed with return code ' + '%i' % returncode) + + +if __name__ == '__main__': + ExampleController().RunController() diff --git a/engine/src/flutter/testing/coverage_util_ios.cc b/engine/src/flutter/testing/coverage_util_ios.cc new file mode 100644 index 0000000000..15ac1b4a0a --- /dev/null +++ b/engine/src/flutter/testing/coverage_util_ios.cc @@ -0,0 +1,15 @@ +// Copyright 2014 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. + +extern "C" void __gcov_flush(); + +namespace coverage_util { + +void FlushCoverageDataIfNecessary() { +#if defined(ENABLE_TEST_CODE_COVERAGE) + __gcov_flush(); +#endif +} + +} // namespace coverage_util diff --git a/engine/src/flutter/testing/coverage_util_ios.h b/engine/src/flutter/testing/coverage_util_ios.h new file mode 100644 index 0000000000..702811aa7b --- /dev/null +++ b/engine/src/flutter/testing/coverage_util_ios.h @@ -0,0 +1,17 @@ +// Copyright 2014 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. + +#ifndef TESTING_COVERAGE_UTIL_IOS_H_ +#define TESTING_COVERAGE_UTIL_IOS_H_ + +namespace coverage_util { + +// Flushes .gcda coverage files if ENABLE_TEST_CODE_COVERAGE is defined. iOS 7 +// does not call any code at the "end" of an app so flushing should be +// performed manually. +void FlushCoverageDataIfNecessary(); + +} // namespace coverage_util + +#endif // TESTING_COVERAGE_UTIL_IOS_H_ diff --git a/engine/src/flutter/testing/generate_gmock_mutant.py b/engine/src/flutter/testing/generate_gmock_mutant.py new file mode 100755 index 0000000000..9c5678c2f2 --- /dev/null +++ b/engine/src/flutter/testing/generate_gmock_mutant.py @@ -0,0 +1,450 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +import string +import sys + +HEADER = """\ +// 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. + +// This file automatically generated by testing/generate_gmock_mutant.py. +// DO NOT EDIT. + +#ifndef TESTING_GMOCK_MUTANT_H_ +#define TESTING_GMOCK_MUTANT_H_ + +// The intention of this file is to make possible using GMock actions in +// all of its syntactic beauty. Classes and helper functions can be used as +// more generic variants of Task and Callback classes (see base/task.h) +// Mutant supports both pre-bound arguments (like Task) and call-time +// arguments (like Callback) - hence the name. :-) +// +// DispatchToMethod/Function supports two sets of arguments: pre-bound (P) and +// call-time (C). The arguments as well as the return type are templatized. +// DispatchToMethod/Function will also try to call the selected method or +// function even if provided pre-bound arguments does not match exactly with +// the function signature hence the X1, X2 ... XN parameters in CreateFunctor. +// DispatchToMethod will try to invoke method that may not belong to the +// object's class itself but to the object's class base class. +// +// Additionally you can bind the object at calltime by binding a pointer to +// pointer to the object at creation time - before including this file you +// have to #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING. +// +// TODO(stoyan): It's yet not clear to me should we use T& and T&* instead +// of T* and T** when we invoke CreateFunctor to match the EXPECT_CALL style. +// +// +// Sample usage with gMock: +// +// struct Mock : public ObjectDelegate { +// MOCK_METHOD2(string, OnRequest(int n, const string& request)); +// MOCK_METHOD1(void, OnQuit(int exit_code)); +// MOCK_METHOD2(void, LogMessage(int level, const string& message)); +// +// string HandleFlowers(const string& reply, int n, const string& request) { +// string result = SStringPrintf("In request of %d %s ", n, request); +// for (int i = 0; i < n; ++i) result.append(reply) +// return result; +// } +// +// void DoLogMessage(int level, const string& message) { +// } +// +// void QuitMessageLoop(int seconds) { +// base::MessageLoop* loop = base::MessageLoop::current(); +// loop->PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), +// 1000 * seconds); +// } +// }; +// +// Mock mock; +// // Will invoke mock.HandleFlowers("orchids", n, request) +// // "orchids" is a pre-bound argument, and and are call-time +// // arguments - they are not known until the OnRequest mock is invoked. +// EXPECT_CALL(mock, OnRequest(Ge(5), StartsWith("flower")) +// .Times(1) +// .WillOnce(Invoke(CreateFunctor(&mock, &Mock::HandleFlowers, +// string("orchids")))); +// +// +// // No pre-bound arguments, two call-time arguments passed +// // directly to DoLogMessage +// EXPECT_CALL(mock, OnLogMessage(_, _)) +// .Times(AnyNumber()) +// .WillAlways(Invoke(CreateFunctor, &mock, &Mock::DoLogMessage)); +// +// +// // In this case we have a single pre-bound argument - 3. We ignore +// // all of the arguments of OnQuit. +// EXCEPT_CALL(mock, OnQuit(_)) +// .Times(1) +// .WillOnce(InvokeWithoutArgs(CreateFunctor( +// &mock, &Mock::QuitMessageLoop, 3))); +// +// MessageLoop loop; +// loop.Run(); +// +// +// // Here is another example of how we can set an action that invokes +// // method of an object that is not yet created. +// struct Mock : public ObjectDelegate { +// MOCK_METHOD1(void, DemiurgeCreated(Demiurge*)); +// MOCK_METHOD2(void, OnRequest(int count, const string&)); +// +// void StoreDemiurge(Demiurge* w) { +// demiurge_ = w; +// } +// +// Demiurge* demiurge; +// } +// +// EXPECT_CALL(mock, DemiurgeCreated(_)).Times(1) +// .WillOnce(Invoke(CreateFunctor(&mock, &Mock::StoreDemiurge))); +// +// EXPECT_CALL(mock, OnRequest(_, StrEq("Moby Dick"))) +// .Times(AnyNumber()) +// .WillAlways(WithArgs<0>(Invoke( +// CreateFunctor(&mock->demiurge_, &Demiurge::DecreaseMonsters)))); +// + +#include "base/memory/linked_ptr.h" +#include "base/tuple.h" + +namespace testing {""" + +MUTANT = """\ + +// Interface that is exposed to the consumer, that does the actual calling +// of the method. +template +class MutantRunner { + public: + virtual R RunWithParams(const Params& params) = 0; + virtual ~MutantRunner() {} +}; + +// Mutant holds pre-bound arguments (like Task). Like Callback +// allows call-time arguments. You bind a pointer to the object +// at creation time. +template +class Mutant : public MutantRunner { + public: + Mutant(T* obj, Method method, const PreBound& pb) + : obj_(obj), method_(method), pb_(pb) { + } + + // MutantRunner implementation + virtual R RunWithParams(const Params& params) { + return DispatchToMethod(this->obj_, this->method_, pb_, params); + } + + T* obj_; + Method method_; + PreBound pb_; +}; + +template +class MutantFunction : public MutantRunner { + public: + MutantFunction(Function function, const PreBound& pb) + : function_(function), pb_(pb) { + } + + // MutantRunner implementation + virtual R RunWithParams(const Params& params) { + return DispatchToFunction(function_, pb_, params); + } + + Function function_; + PreBound pb_; +}; + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +// MutantLateBind is like Mutant, but you bind a pointer to a pointer +// to the object. This way you can create actions for an object +// that is not yet created (has only storage for a pointer to it). +template +class MutantLateObjectBind : public MutantRunner { + public: + MutantLateObjectBind(T** obj, Method method, const PreBound& pb) + : obj_(obj), method_(method), pb_(pb) { + } + + // MutantRunner implementation. + virtual R RunWithParams(const Params& params) { + EXPECT_THAT(*this->obj_, testing::NotNull()); + if (NULL == *this->obj_) + return R(); + return DispatchToMethod( *this->obj_, this->method_, pb_, params); + } + + T** obj_; + Method method_; + PreBound pb_; +}; +#endif + +// Simple MutantRunner<> wrapper acting as a functor. +// Redirects operator() to MutantRunner::Run() +template +struct MutantFunctor { + explicit MutantFunctor(MutantRunner* cb) : impl_(cb) { + } + + ~MutantFunctor() { + } + + inline R operator()() { + return impl_->RunWithParams(base::Tuple<>()); + } + + template + inline R operator()(const Arg1& a) { + return impl_->RunWithParams(Params(a)); + } + + template + inline R operator()(const Arg1& a, const Arg2& b) { + return impl_->RunWithParams(Params(a, b)); + } + + template + inline R operator()(const Arg1& a, const Arg2& b, const Arg3& c) { + return impl_->RunWithParams(Params(a, b, c)); + } + + template + inline R operator()(const Arg1& a, const Arg2& b, const Arg3& c, + const Arg4& d) { + return impl_->RunWithParams(Params(a, b, c, d)); + } + + private: + // We need copy constructor since MutantFunctor is copied few times + // inside GMock machinery, hence no DISALLOW_EVIL_CONTRUCTORS + MutantFunctor(); + linked_ptr > impl_; +}; +""" + +FOOTER = """\ +} // namespace testing + +#endif // TESTING_GMOCK_MUTANT_H_""" + +# Templates for DispatchToMethod/DispatchToFunction functions. +# template_params - typename P1, typename P2.. typename C1.. +# prebound - Tuple +# calltime - Tuple +# args - p.a, p.b.., c.a, c.b.. +DISPATCH_TO_METHOD_TEMPLATE = """\ +template +inline R DispatchToMethod(T* obj, Method method, + const %(prebound)s& p, + const %(calltime)s& c) { + return (obj->*method)(%(args)s); +} +""" + +DISPATCH_TO_FUNCTION_TEMPLATE = """\ +template +inline R DispatchToFunction(Function function, + const %(prebound)s& p, + const %(calltime)s& c) { + return (*function)(%(args)s); +} +""" + +# Templates for CreateFunctor functions. +# template_params - typename P1, typename P2.. typename C1.. typename X1.. +# prebound - Tuple +# calltime - Tuple +# params - X1,.. , A1, .. +# args - const P1& p1 .. +# call_args - p1, p2, p3.. +CREATE_METHOD_FUNCTOR_TEMPLATE = """\ +template +inline MutantFunctor +CreateFunctor(T* obj, R (U::*method)(%(params)s), %(args)s) { + MutantRunner* t = + new Mutant + (obj, method, base::MakeTuple(%(call_args)s)); + return MutantFunctor(t); +} +""" + +CREATE_FUNCTION_FUNCTOR_TEMPLATE = """\ +template +inline MutantFunctor +CreateFunctor(R (*function)(%(params)s), %(args)s) { + MutantRunner* t = + new MutantFunction + (function, base::MakeTuple(%(call_args)s)); + return MutantFunctor(t); +} +""" + +def SplitLine(line, width): + """Splits a single line at comma, at most |width| characters long.""" + if len(line) <= width: + return (line, None) + n = 1 + line[:width].rfind(",") + if n == 0: # If comma cannot be found give up and return the entire line. + return (line, None) + # Assume there is a space after the comma + assert line[n] == " " + return (line[:n], line[n + 1:]) + + +def Wrap(s, width, subsequent_offset): + """Wraps a single line |s| at commas so every line is at most |width| + characters long. + """ + w = [] + spaces = " " * subsequent_offset + while s: + (f, s) = SplitLine(s, width) + w.append(f) + if s: + s = spaces + s + return "\n".join(w) + + +def Clean(s): + """Cleans artifacts from generated C++ code. + + Our simple string formatting/concatenation may introduce extra commas. + """ + s = s.replace(", >", ">") + s = s.replace(", )", ")") + return s + + +def ExpandPattern(pattern, it): + """Return list of expanded pattern strings. + + Each string is created by replacing all '%' in |pattern| with element of |it|. + """ + return [pattern.replace("%", x) for x in it] + + +def Gen(pattern, n, start): + """Expands pattern replacing '%' with sequential integers starting with start. + + Expanded patterns will be joined with comma separator. + Gen("X%", 3, 1) will return "X1, X2, X3". + """ + it = string.hexdigits[start:n + start] + return ", ".join(ExpandPattern(pattern, it)) + + +def Merge(a): + return ", ".join(filter(len, a)) + + +def GenTuple(pattern, n): + return Clean("base::Tuple<%s>" % (Gen(pattern, n, 1))) + + +def FixCode(s): + lines = Clean(s).splitlines() + # Wrap sometimes very long 1st line to be inside the "template <" + lines[0] = Wrap(lines[0], 80, 10) + + # Wrap all subsequent lines to 6 spaces arbitrarily. This is a 2-space line + # indent, plus a 4 space continuation indent. + for line in xrange(1, len(lines)): + lines[line] = Wrap(lines[line], 80, 6) + return "\n".join(lines) + + +def GenerateDispatch(prebound, calltime): + print "\n// %d - %d" % (prebound, calltime) + args = { + "template_params": Merge([Gen("typename P%", prebound, 1), + Gen("typename C%", calltime, 1)]), + "prebound": GenTuple("P%", prebound), + "calltime": GenTuple("C%", calltime), + "args": Merge([Gen("base::get<%>(p)", prebound, 0), + Gen("base::get<%>(c)", calltime, 0)]), + } + + print FixCode(DISPATCH_TO_METHOD_TEMPLATE % args) + print FixCode(DISPATCH_TO_FUNCTION_TEMPLATE % args) + + +def GenerateCreateFunctor(prebound, calltime): + print "// %d - %d" % (prebound, calltime) + args = { + "calltime": GenTuple("A%", calltime), + "prebound": GenTuple("P%", prebound), + "params": Merge([Gen("X%", prebound, 1), Gen("A%", calltime, 1)]), + "args": Gen("const P%& p%", prebound, 1), + "call_args": Gen("p%", prebound, 1), + "template_params": Merge([Gen("typename P%", prebound, 1), + Gen("typename A%", calltime, 1), + Gen("typename X%", prebound, 1)]) + } + + mutant = FixCode(CREATE_METHOD_FUNCTOR_TEMPLATE % args) + print mutant + + # Slightly different version for free function call. + print "\n", FixCode(CREATE_FUNCTION_FUNCTOR_TEMPLATE % args) + + # Functor with pointer to a pointer of the object. + print "\n#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING" + mutant2 = mutant.replace("CreateFunctor(T* obj,", "CreateFunctor(T** obj,") + mutant2 = mutant2.replace("new Mutant", "new MutantLateObjectBind") + mutant2 = mutant2.replace(" " * 17 + "Tuple", " " * 31 + "Tuple") + print mutant2 + print "#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING\n" + + # OS_WIN specific. Same functors but with stdcall calling conventions. + # These are not for WIN64 (x86_64) because there is only one calling + # convention in WIN64. + # Functor for method with __stdcall calling conventions. + print "#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64)" + stdcall_method = CREATE_METHOD_FUNCTOR_TEMPLATE + stdcall_method = stdcall_method.replace("U::", "__stdcall U::") + stdcall_method = FixCode(stdcall_method % args) + print stdcall_method + # Functor for free function with __stdcall calling conventions. + stdcall_function = CREATE_FUNCTION_FUNCTOR_TEMPLATE + stdcall_function = stdcall_function.replace("R (*", "R (__stdcall *") + print "\n", FixCode(stdcall_function % args) + + print "#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING" + stdcall2 = stdcall_method + stdcall2 = stdcall2.replace("CreateFunctor(T* obj,", "CreateFunctor(T** obj,") + stdcall2 = stdcall2.replace("new Mutant", "new MutantLateObjectBind") + stdcall2 = stdcall2.replace(" " * 17 + "Tuple", " " * 31 + "Tuple") + print stdcall2 + print "#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING" + print "#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64)\n" + + +def main(): + print HEADER + for prebound in xrange(0, 6 + 1): + for args in xrange(0, 6 + 1): + GenerateDispatch(prebound, args) + print MUTANT + for prebound in xrange(0, 6 + 1): + for args in xrange(0, 6 + 1): + GenerateCreateFunctor(prebound, args) + print FOOTER + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/engine/src/flutter/testing/gmock.gyp b/engine/src/flutter/testing/gmock.gyp new file mode 100644 index 0000000000..6363b4b183 --- /dev/null +++ b/engine/src/flutter/testing/gmock.gyp @@ -0,0 +1,67 @@ +# Copyright (c) 2009 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. + +{ + 'targets': [ + { + 'target_name': 'gmock', + 'type': 'static_library', + 'dependencies': [ + 'gtest.gyp:gtest', + ], + 'sources': [ + # Sources based on files in r173 of gmock. + 'gmock/include/gmock/gmock-actions.h', + 'gmock/include/gmock/gmock-cardinalities.h', + 'gmock/include/gmock/gmock-generated-actions.h', + 'gmock/include/gmock/gmock-generated-function-mockers.h', + 'gmock/include/gmock/gmock-generated-matchers.h', + 'gmock/include/gmock/gmock-generated-nice-strict.h', + 'gmock/include/gmock/gmock-matchers.h', + 'gmock/include/gmock/gmock-spec-builders.h', + 'gmock/include/gmock/gmock.h', + 'gmock/include/gmock/internal/gmock-generated-internal-utils.h', + 'gmock/include/gmock/internal/gmock-internal-utils.h', + 'gmock/include/gmock/internal/gmock-port.h', + 'gmock/src/gmock-all.cc', + 'gmock/src/gmock-cardinalities.cc', + 'gmock/src/gmock-internal-utils.cc', + 'gmock/src/gmock-matchers.cc', + 'gmock/src/gmock-spec-builders.cc', + 'gmock/src/gmock.cc', + 'gmock_mutant.h', # gMock helpers + ], + 'sources!': [ + 'gmock/src/gmock-all.cc', # Not needed by our build. + ], + 'include_dirs': [ + 'gmock', + 'gmock/include', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + 'gmock/include', # So that gmock headers can find themselves. + ], + }, + 'export_dependent_settings': [ + 'gtest.gyp:gtest', + ], + 'conditions': [ + ['OS == "ios"', { + 'toolsets': ['host', 'target'], + }], + ], + }, + { + 'target_name': 'gmock_main', + 'type': 'static_library', + 'dependencies': [ + 'gmock', + ], + 'sources': [ + 'gmock/src/gmock_main.cc', + ], + }, + ], +} diff --git a/engine/src/flutter/testing/gmock_mutant.h b/engine/src/flutter/testing/gmock_mutant.h new file mode 100644 index 0000000000..acc1ae9249 --- /dev/null +++ b/engine/src/flutter/testing/gmock_mutant.h @@ -0,0 +1,5177 @@ +// 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. + +// This file automatically generated by testing/generate_gmock_mutant.py. +// DO NOT EDIT. + +#ifndef TESTING_GMOCK_MUTANT_H_ +#define TESTING_GMOCK_MUTANT_H_ + +// The intention of this file is to make possible using GMock actions in +// all of its syntactic beauty. Classes and helper functions can be used as +// more generic variants of Task and Callback classes (see base/task.h) +// Mutant supports both pre-bound arguments (like Task) and call-time +// arguments (like Callback) - hence the name. :-) +// +// DispatchToMethod/Function supports two sets of arguments: pre-bound (P) and +// call-time (C). The arguments as well as the return type are templatized. +// DispatchToMethod/Function will also try to call the selected method or +// function even if provided pre-bound arguments does not match exactly with +// the function signature hence the X1, X2 ... XN parameters in CreateFunctor. +// DispatchToMethod will try to invoke method that may not belong to the +// object's class itself but to the object's class base class. +// +// Additionally you can bind the object at calltime by binding a pointer to +// pointer to the object at creation time - before including this file you +// have to #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING. +// +// TODO(stoyan): It's yet not clear to me should we use T& and T&* instead +// of T* and T** when we invoke CreateFunctor to match the EXPECT_CALL style. +// +// +// Sample usage with gMock: +// +// struct Mock : public ObjectDelegate { +// MOCK_METHOD2(string, OnRequest(int n, const string& request)); +// MOCK_METHOD1(void, OnQuit(int exit_code)); +// MOCK_METHOD2(void, LogMessage(int level, const string& message)); +// +// string HandleFlowers(const string& reply, int n, const string& request) { +// string result = SStringPrintf("In request of %d %s ", n, request); +// for (int i = 0; i < n; ++i) result.append(reply) +// return result; +// } +// +// void DoLogMessage(int level, const string& message) { +// } +// +// void QuitMessageLoop(int seconds) { +// base::MessageLoop* loop = base::MessageLoop::current(); +// loop->PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), +// 1000 * seconds); +// } +// }; +// +// Mock mock; +// // Will invoke mock.HandleFlowers("orchids", n, request) +// // "orchids" is a pre-bound argument, and and are call-time +// // arguments - they are not known until the OnRequest mock is invoked. +// EXPECT_CALL(mock, OnRequest(Ge(5), StartsWith("flower")) +// .Times(1) +// .WillOnce(Invoke(CreateFunctor(&mock, &Mock::HandleFlowers, +// string("orchids")))); +// +// +// // No pre-bound arguments, two call-time arguments passed +// // directly to DoLogMessage +// EXPECT_CALL(mock, OnLogMessage(_, _)) +// .Times(AnyNumber()) +// .WillAlways(Invoke(CreateFunctor, &mock, &Mock::DoLogMessage)); +// +// +// // In this case we have a single pre-bound argument - 3. We ignore +// // all of the arguments of OnQuit. +// EXCEPT_CALL(mock, OnQuit(_)) +// .Times(1) +// .WillOnce(InvokeWithoutArgs(CreateFunctor( +// &mock, &Mock::QuitMessageLoop, 3))); +// +// MessageLoop loop; +// loop.Run(); +// +// +// // Here is another example of how we can set an action that invokes +// // method of an object that is not yet created. +// struct Mock : public ObjectDelegate { +// MOCK_METHOD1(void, DemiurgeCreated(Demiurge*)); +// MOCK_METHOD2(void, OnRequest(int count, const string&)); +// +// void StoreDemiurge(Demiurge* w) { +// demiurge_ = w; +// } +// +// Demiurge* demiurge; +// } +// +// EXPECT_CALL(mock, DemiurgeCreated(_)).Times(1) +// .WillOnce(Invoke(CreateFunctor(&mock, &Mock::StoreDemiurge))); +// +// EXPECT_CALL(mock, OnRequest(_, StrEq("Moby Dick"))) +// .Times(AnyNumber()) +// .WillAlways(WithArgs<0>(Invoke( +// CreateFunctor(&mock->demiurge_, &Demiurge::DecreaseMonsters)))); +// + +#include "base/memory/linked_ptr.h" +#include "base/tuple.h" + +namespace testing { + +// 0 - 0 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple<>& p, + const base::Tuple<>& c) { + return (obj->*method)(); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple<>& p, + const base::Tuple<>& c) { + return (*function)(); +} + +// 0 - 1 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple<>& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple<>& p, + const base::Tuple& c) { + return (*function)(base::get<0>(c)); +} + +// 0 - 2 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple<>& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(c), base::get<1>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple<>& p, + const base::Tuple& c) { + return (*function)(base::get<0>(c), base::get<1>(c)); +} + +// 0 - 3 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple<>& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(c), base::get<1>(c), base::get<2>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple<>& p, + const base::Tuple& c) { + return (*function)(base::get<0>(c), base::get<1>(c), base::get<2>(c)); +} + +// 0 - 4 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple<>& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple<>& p, + const base::Tuple& c) { + return (*function)(base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c)); +} + +// 0 - 5 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple<>& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c), base::get<4>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple<>& p, + const base::Tuple& c) { + return (*function)(base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c), base::get<4>(c)); +} + +// 0 - 6 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple<>& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c), base::get<4>(c), base::get<5>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple<>& p, + const base::Tuple& c) { + return (*function)(base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c), base::get<4>(c), base::get<5>(c)); +} + +// 1 - 0 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple<>& c) { + return (obj->*method)(base::get<0>(p)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple<>& c) { + return (*function)(base::get<0>(p)); +} + +// 1 - 1 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<0>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<0>(c)); +} + +// 1 - 2 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<0>(c), base::get<1>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<0>(c), base::get<1>(c)); +} + +// 1 - 3 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c)); +} + +// 1 - 4 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c)); +} + +// 1 - 5 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c), base::get<4>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c), base::get<4>(c)); +} + +// 1 - 6 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c), base::get<4>(c), base::get<5>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c), base::get<4>(c), base::get<5>(c)); +} + +// 2 - 0 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple<>& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple<>& c) { + return (*function)(base::get<0>(p), base::get<1>(p)); +} + +// 2 - 1 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<0>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<0>(c)); +} + +// 2 - 2 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c)); +} + +// 2 - 3 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c)); +} + +// 2 - 4 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c)); +} + +// 2 - 5 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c), base::get<4>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c), base::get<4>(c)); +} + +// 2 - 6 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c), base::get<4>(c), + base::get<5>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c), base::get<4>(c), + base::get<5>(c)); +} + +// 3 - 0 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple<>& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple<>& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p)); +} + +// 3 - 1 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c)); +} + +// 3 - 2 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c)); +} + +// 3 - 3 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c), base::get<2>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c), base::get<2>(c)); +} + +// 3 - 4 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c), base::get<2>(c), base::get<3>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c), base::get<2>(c), base::get<3>(c)); +} + +// 3 - 5 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c), base::get<2>(c), base::get<3>(c), + base::get<4>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c), base::get<2>(c), base::get<3>(c), + base::get<4>(c)); +} + +// 3 - 6 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c), base::get<2>(c), base::get<3>(c), + base::get<4>(c), base::get<5>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<0>(c), base::get<1>(c), base::get<2>(c), base::get<3>(c), + base::get<4>(c), base::get<5>(c)); +} + +// 4 - 0 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple<>& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple<>& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p)); +} + +// 4 - 1 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c)); +} + +// 4 - 2 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c)); +} + +// 4 - 3 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c), base::get<2>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c), base::get<2>(c)); +} + +// 4 - 4 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c)); +} + +// 4 - 5 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c), base::get<4>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c), base::get<4>(c)); +} + +// 4 - 6 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c), base::get<4>(c), base::get<5>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<0>(c), base::get<1>(c), base::get<2>(c), + base::get<3>(c), base::get<4>(c), base::get<5>(c)); +} + +// 5 - 0 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple<>& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple<>& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p)); +} + +// 5 - 1 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c)); +} + +// 5 - 2 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c)); +} + +// 5 - 3 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c)); +} + +// 5 - 4 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c)); +} + +// 5 - 5 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c), base::get<4>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c), base::get<4>(c)); +} + +// 5 - 6 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c), base::get<4>(c), base::get<5>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<0>(c), base::get<1>(c), + base::get<2>(c), base::get<3>(c), base::get<4>(c), base::get<5>(c)); +} + +// 6 - 0 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple<>& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple<>& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p)); +} + +// 6 - 1 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c)); +} + +// 6 - 2 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c)); +} + +// 6 - 3 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c)); +} + +// 6 - 4 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c)); +} + +// 6 - 5 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c), base::get<4>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c), base::get<4>(c)); +} + +// 6 - 6 +template +inline R DispatchToMethod(T* obj, Method method, + const base::Tuple& p, + const base::Tuple& c) { + return (obj->*method)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c), base::get<4>(c), + base::get<5>(c)); +} +template +inline R DispatchToFunction(Function function, + const base::Tuple& p, + const base::Tuple& c) { + return (*function)(base::get<0>(p), base::get<1>(p), base::get<2>(p), + base::get<3>(p), base::get<4>(p), base::get<5>(p), base::get<0>(c), + base::get<1>(c), base::get<2>(c), base::get<3>(c), base::get<4>(c), + base::get<5>(c)); +} + +// Interface that is exposed to the consumer, that does the actual calling +// of the method. +template +class MutantRunner { + public: + virtual R RunWithParams(const Params& params) = 0; + virtual ~MutantRunner() {} +}; + +// Mutant holds pre-bound arguments (like Task). Like Callback +// allows call-time arguments. You bind a pointer to the object +// at creation time. +template +class Mutant : public MutantRunner { + public: + Mutant(T* obj, Method method, const PreBound& pb) + : obj_(obj), method_(method), pb_(pb) { + } + + // MutantRunner implementation + virtual R RunWithParams(const Params& params) { + return DispatchToMethod(this->obj_, this->method_, pb_, params); + } + + T* obj_; + Method method_; + PreBound pb_; +}; + +template +class MutantFunction : public MutantRunner { + public: + MutantFunction(Function function, const PreBound& pb) + : function_(function), pb_(pb) { + } + + // MutantRunner implementation + virtual R RunWithParams(const Params& params) { + return DispatchToFunction(function_, pb_, params); + } + + Function function_; + PreBound pb_; +}; + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +// MutantLateBind is like Mutant, but you bind a pointer to a pointer +// to the object. This way you can create actions for an object +// that is not yet created (has only storage for a pointer to it). +template +class MutantLateObjectBind : public MutantRunner { + public: + MutantLateObjectBind(T** obj, Method method, const PreBound& pb) + : obj_(obj), method_(method), pb_(pb) { + } + + // MutantRunner implementation. + virtual R RunWithParams(const Params& params) { + EXPECT_THAT(*this->obj_, testing::NotNull()); + if (NULL == *this->obj_) + return R(); + return DispatchToMethod( *this->obj_, this->method_, pb_, params); + } + + T** obj_; + Method method_; + PreBound pb_; +}; +#endif + +// Simple MutantRunner<> wrapper acting as a functor. +// Redirects operator() to MutantRunner::Run() +template +struct MutantFunctor { + explicit MutantFunctor(MutantRunner* cb) : impl_(cb) { + } + + ~MutantFunctor() { + } + + inline R operator()() { + return impl_->RunWithParams(base::Tuple<>()); + } + + template + inline R operator()(const Arg1& a) { + return impl_->RunWithParams(Params(a)); + } + + template + inline R operator()(const Arg1& a, const Arg2& b) { + return impl_->RunWithParams(Params(a, b)); + } + + template + inline R operator()(const Arg1& a, const Arg2& b, const Arg3& c) { + return impl_->RunWithParams(Params(a, b, c)); + } + + template + inline R operator()(const Arg1& a, const Arg2& b, const Arg3& c, + const Arg4& d) { + return impl_->RunWithParams(Params(a, b, c, d)); + } + + private: + // We need copy constructor since MutantFunctor is copied few times + // inside GMock machinery, hence no DISALLOW_EVIL_CONTRUCTORS + MutantFunctor(); + linked_ptr > impl_; +}; + +// 0 - 0 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)()) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)()) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)()) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)()) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)()) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)()) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 0 - 1 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(A1)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(A1)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(A1)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(A1)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(A1)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(A1)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 0 - 2 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(A1, A2)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(A1, A2)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(A1, A2)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(A1, A2)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(A1, A2)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(A1, A2)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 0 - 3 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(A1, A2, A3)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(A1, A2, A3)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(A1, A2, A3)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(A1, A2, A3)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(A1, A2, A3)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(A1, A2, A3)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 0 - 4 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(A1, A2, A3, A4)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(A1, A2, A3, A4)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(A1, A2, A3, A4)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(A1, A2, A3, A4)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(A1, A2, A3, A4)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(A1, A2, A3, A4)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 0 - 5 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(A1, A2, A3, A4, A5)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(A1, A2, A3, A4, A5)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(A1, A2, A3, A4, A5)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(A1, A2, A3, A4, A5)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(A1, A2, A3, A4, A5)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(A1, A2, A3, A4, A5)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 0 - 6 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(A1, A2, A3, A4, A5, A6)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(A1, A2, A3, A4, A5, A6)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(A1, A2, A3, A4, A5, A6)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(A1, A2, A3, A4, A5, A6)) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(A1, A2, A3, A4, A5, A6)) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple()); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(A1, A2, A3, A4, A5, A6)) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple()); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 1 - 0 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 1 - 1 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, A1), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, A1), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, A1), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, A1), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, A1), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, A1), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 1 - 2 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, A1, A2), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, A1, A2), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, A1, A2), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, A1, A2), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, A1, A2), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, A1, A2), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 1 - 3 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, A1, A2, A3), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, A1, A2, A3), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, A1, A2, A3), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, A1, A2, A3), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, A1, A2, A3), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, A1, A2, A3), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 1 - 4 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, A1, A2, A3, A4), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, A1, A2, A3, A4), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, A1, A2, A3, A4), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, A1, A2, A3, A4), + const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, A1, A2, A3, A4), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, A1, A2, A3, A4), + const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 1 - 5 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, A1, A2, A3, A4, A5), const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, A1, A2, A3, A4, A5), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, A1, A2, A3, A4, A5), const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, A1, A2, A3, A4, A5), + const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, A1, A2, A3, A4, A5), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, A1, A2, A3, A4, A5), + const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 1 - 6 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, A1, A2, A3, A4, A5, A6), + const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, A1, A2, A3, A4, A5, A6), const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, A1, A2, A3, A4, A5, A6), + const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, A1, A2, A3, A4, A5, A6), + const P1& p1) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, A1, A2, A3, A4, A5, A6), + const P1& p1) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, A1, A2, A3, A4, A5, A6), + const P1& p1) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 2 - 0 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2), const P1& p1, const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2), const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2), const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2), const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 2 - 1 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, A1), const P1& p1, const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, A1), const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, A1), const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, A1), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, A1), const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, A1), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 2 - 2 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, A1, A2), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, A1, A2), const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, A1, A2), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, A1, A2), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, A1, A2), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, A1, A2), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 2 - 3 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, A1, A2, A3), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, A1, A2, A3), const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, A1, A2, A3), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, A1, A2, A3), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, A1, A2, A3), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, A1, A2, A3), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 2 - 4 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, A1, A2, A3, A4), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, A1, A2, A3, A4), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, A1, A2, A3, A4), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, A1, A2, A3, A4), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, A1, A2, A3, A4), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, A1, A2, A3, A4), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 2 - 5 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, A1, A2, A3, A4, A5), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, A1, A2, A3, A4, A5), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, A1, A2, A3, A4, A5), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, A1, A2, A3, A4, A5), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 2 - 6 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, A1, A2, A3, A4, A5, A6), const P1& p1, + const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 3 - 0 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3), const P1& p1, const P2& p2, + const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3), const P1& p1, const P2& p2, + const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3), const P1& p1, const P2& p2, + const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3), const P1& p1, const P2& p2, + const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 3 - 1 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, A1), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, A1), const P1& p1, const P2& p2, + const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, A1), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, A1), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, A1), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, A1), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 3 - 2 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, A1, A2), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, A1, A2), const P1& p1, const P2& p2, + const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, A1, A2), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, A1, A2), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 3 - 3 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, A1, A2, A3), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, A1, A2, A3), const P1& p1, const P2& p2, + const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, A1, A2, A3), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, A1, A2, A3), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 3 - 4 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, A1, A2, A3, A4), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, A1, A2, A3, A4), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, A1, A2, A3, A4), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, A1, A2, A3, A4), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 3 - 5 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, A1, A2, A3, A4, A5), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 3 - 6 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, A1, A2, A3, A4, A5, A6), const P1& p1, + const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2, A3, A4, A5, + A6), const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, A1, A2, A3, A4, A5, + A6), const P1& p1, const P2& p2, const P3& p3) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 4 - 0 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4), const P1& p1, const P2& p2, + const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 4 - 1 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, A1), const P1& p1, const P2& p2, + const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 4 - 2 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, A1, A2), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, A1, A2), const P1& p1, const P2& p2, + const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, A1, A2), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, A1, A2), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 4 - 3 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, A1, A2, A3), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, A1, A2, A3), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, A1, A2, A3), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, A1, A2, A3), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 4 - 4 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, A1, A2, A3, A4), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 4 - 5 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, A1, A2, A3, A4, A5), const P1& p1, + const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2, A3, A4, + A5), const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2, A3, A4, + A5), const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 4 - 6 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2, A3, A4, + A5, A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, A1, A2, A3, A4, + A5, A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 5 - 0 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5), const P1& p1, const P2& p2, + const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 5 - 1 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, A1), const P1& p1, const P2& p2, + const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 5 - 2 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, A1, A2), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, A1, A2), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 5 - 3 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, A1, A2, A3), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 5 - 4 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, A1, A2, A3, A4), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, + A4), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, + A4), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 5 - 5 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, + A4, A5), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, + A4, A5), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 5 - 6 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, A4, A5, + A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, A4, A5, + A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, + A4, A5, A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, A1, A2, A3, A4, A5, + A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, A1, A2, A3, + A4, A5, A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 6 - 0 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, X6), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, X6), const P1& p1, const P2& p2, + const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, X6), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, X6), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple<>> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple<>> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 6 - 1 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, X6, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, X6, A1), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 6 - 2 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, X6, A1, A2), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, X6, A1, A2), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 6 - 3 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, X6, A1, A2, A3), const P1& p1, + const P2& p2, const P3& p3, const P4& p4, const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, + A3), const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, X6, A1, A2, A3), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, + A3), const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 6 - 4 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, + A3, A4), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5, const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, + A3, A4), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 6 - 5 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4, + A5), const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4, A5), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4, + A5), const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, + A3, A4, A5), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5, const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4, + A5), const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, + A3, A4, A5), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +// 6 - 6 +template +inline MutantFunctor> +CreateFunctor(T* obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4, A5, + A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (*function)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4, A5, A6), + const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4, A5, + A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, const P5& p5, + const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING + +#if defined (OS_WIN) && !defined (ARCH_CPU_X86_64) +template +inline MutantFunctor> +CreateFunctor(T* obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, + A3, A4, A5, A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5, const P6& p6) { + MutantRunner>* t = + new Mutant, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} + +template +inline MutantFunctor> +CreateFunctor(R (__stdcall *function)(X1, X2, X3, X4, X5, X6, A1, A2, A3, A4, + A5, A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantFunction, base::Tuple> + (function, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#ifdef GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +template +inline MutantFunctor> +CreateFunctor(T** obj, R (__stdcall U::*method)(X1, X2, X3, X4, X5, X6, A1, A2, + A3, A4, A5, A6), const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5, const P6& p6) { + MutantRunner>* t = + new MutantLateObjectBind, base::Tuple> + (obj, method, base::MakeTuple(p1, p2, p3, p4, p5, p6)); + return MutantFunctor>(t); +} +#endif // GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#endif // defined (OS_WIN) && !defined (ARCH_CPU_X86_64) + +} // namespace testing + +#endif // TESTING_GMOCK_MUTANT_H_ diff --git a/engine/src/flutter/testing/gtest.gyp b/engine/src/flutter/testing/gtest.gyp new file mode 100644 index 0000000000..d61772ecbe --- /dev/null +++ b/engine/src/flutter/testing/gtest.gyp @@ -0,0 +1,214 @@ +# Copyright (c) 2012 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. + +{ + 'includes': [ + 'gtest.gypi', + ], + 'targets': [ + { + 'target_name': 'gtest', + 'toolsets': ['host', 'target'], + 'type': 'static_library', + 'sources': [ + '<@(gtest_sources)', + ], + 'include_dirs': [ + 'gtest', + 'gtest/include', + ], + 'dependencies': [ + 'gtest_prod', + ], + 'defines': [ + # In order to allow regex matches in gtest to be shared between Windows + # and other systems, we tell gtest to always use it's internal engine. + 'GTEST_HAS_POSIX_RE=0', + # Chrome doesn't support / require C++11, yet. + 'GTEST_LANG_CXX11=0', + ], + 'all_dependent_settings': { + 'defines': [ + 'GTEST_HAS_POSIX_RE=0', + 'GTEST_LANG_CXX11=0', + ], + }, + 'conditions': [ + ['OS == "mac" or OS == "ios"', { + 'sources': [ + 'gtest_mac.h', + 'gtest_mac.mm', + 'platform_test_mac.mm', + ], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Foundation.framework', + ], + }, + }], + ['OS == "ios"', { + 'dependencies' : [ + '<(DEPTH)/testing/iossim/iossim.gyp:iossim#host', + ], + 'direct_dependent_settings': { + 'target_conditions': [ + # Turn all tests into bundles on iOS because that's the only + # type of executable supported for iOS. + ['_type=="executable"', { + 'variables': { + # Use a variable so the path gets fixed up so it is always + # correct when INFOPLIST_FILE finally gets set. + 'ios_unittest_info_plist_path': + '<(DEPTH)/testing/gtest_ios/unittest-Info.plist', + }, + 'mac_bundle': 1, + 'xcode_settings': { + 'BUNDLE_ID_TEST_NAME': + '>!(echo ">(_target_name)" | sed -e "s/_//g")', + 'INFOPLIST_FILE': '>(ios_unittest_info_plist_path)', + }, + 'mac_bundle_resources': [ + '<(ios_unittest_info_plist_path)', + '<(DEPTH)/testing/gtest_ios/Default.png', + ], + 'mac_bundle_resources!': [ + '<(ios_unittest_info_plist_path)', + ], + }], + ], + }, + 'sources': [ + 'coverage_util_ios.cc', + 'coverage_util_ios.h', + ], + }], + ['OS=="ios" and asan==1', { + 'direct_dependent_settings': { + 'target_conditions': [ + # Package the ASan runtime dylib into the test app bundles. + ['_type=="executable"', { + 'postbuilds': [ + { + 'variables': { + # Define copy_asan_dylib_path in a variable ending in + # _path so that gyp understands it's a path and + # performs proper relativization during dict merging. + 'copy_asan_dylib_path': + '<(DEPTH)/build/mac/copy_asan_runtime_dylib.sh', + }, + 'postbuild_name': 'Copy ASan runtime dylib', + 'action': [ + '>(copy_asan_dylib_path)', + ], + }, + ], + }], + ], + }, + }], + ['os_posix == 1', { + 'defines': [ + # gtest isn't able to figure out when RTTI is disabled for gcc + # versions older than 4.3.2, and assumes it's enabled. Our Mac + # and Linux builds disable RTTI, and cannot guarantee that the + # compiler will be 4.3.2. or newer. The Mac, for example, uses + # 4.2.1 as that is the latest available on that platform. gtest + # must be instructed that RTTI is disabled here, and for any + # direct dependents that might include gtest headers. + 'GTEST_HAS_RTTI=0', + ], + 'direct_dependent_settings': { + 'defines': [ + 'GTEST_HAS_RTTI=0', + ], + }, + }], + ['OS=="android" and android_app_abi=="x86"', { + 'defines': [ + 'GTEST_HAS_CLONE=0', + ], + 'direct_dependent_settings': { + 'defines': [ + 'GTEST_HAS_CLONE=0', + ], + }, + }], + ['OS=="android"', { + # We want gtest features that use tr1::tuple, but we currently + # don't support the variadic templates used by libstdc++'s + # implementation. gtest supports this scenario by providing its + # own implementation but we must opt in to it. + 'defines': [ + 'GTEST_USE_OWN_TR1_TUPLE=1', + # GTEST_USE_OWN_TR1_TUPLE only works if GTEST_HAS_TR1_TUPLE is set. + # gtest r625 made it so that GTEST_HAS_TR1_TUPLE is set to 0 + # automatically on android, so it has to be set explicitly here. + 'GTEST_HAS_TR1_TUPLE=1', + ], + 'direct_dependent_settings': { + 'defines': [ + 'GTEST_USE_OWN_TR1_TUPLE=1', + 'GTEST_HAS_TR1_TUPLE=1', + ], + }, + }], + ], + 'direct_dependent_settings': { + 'defines': [ + 'UNIT_TEST', + ], + 'include_dirs': [ + 'gtest/include', # So that gtest headers can find themselves. + ], + 'target_conditions': [ + ['_type=="executable"', { + 'test': 1, + 'conditions': [ + ['OS=="mac"', { + 'run_as': { + 'action????': ['${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}'], + }, + }], + ['OS=="ios"', { + 'variables': { + # Use a variable so the path gets fixed up so it is always + # correct when the action finally gets used. + 'ios_run_unittest_script_path': + '<(DEPTH)/testing/gtest_ios/run-unittest.sh', + }, + 'run_as': { + 'action????': ['>(ios_run_unittest_script_path)'], + }, + }], + ['OS=="win"', { + 'run_as': { + 'action????': ['$(TargetPath)', '--gtest_print_time'], + }, + }], + ], + }], + ], + 'msvs_disabled_warnings': [4800], + }, + }, + { + 'target_name': 'gtest_main', + 'type': 'static_library', + 'dependencies': [ + 'gtest', + ], + 'sources': [ + 'gtest/src/gtest_main.cc', + ], + }, + { + 'target_name': 'gtest_prod', + 'toolsets': ['host', 'target'], + 'type': 'none', + 'sources': [ + 'gtest/include/gtest/gtest_prod.h', + ], + }, + ], +} diff --git a/engine/src/flutter/testing/gtest_ios/Default.png b/engine/src/flutter/testing/gtest_ios/Default.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9089d5c6789aae633902ca93ad552f14bfadd7 GIT binary patch literal 1707 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sU@72WW?*2n3R7Lez`!6`;u=vBoS#-wo>-L1 z;Fyx1l&avFo0y&&l$w}QS$HzlhJk^ZA;2fZ^*;!l`Tu|B|Nm+K{~Q1R&+z}u|Nm#s z{691E|CzM^XN>=!VE{?aJoA6%%>Ogf{?9c2Ka&BZHto#+w3+|Y(*CCz|4(B8X*E9c z-+1PKr89*TilAf6c0>)`+44{BCJ_EAWI4#ZCIL(+Ljp2V9!x0^*|JhV3i~;(eJQF_y3Sx$OB8OdvLqxQ6q9p3G&CVvbk$ zZ;8|yuz=V!z&}(P7~ULW(4j?kl#IB{an^LB{Ts5Ud5>N literal 0 HcmV?d00001 diff --git a/engine/src/flutter/testing/gtest_ios/run-unittest.sh b/engine/src/flutter/testing/gtest_ios/run-unittest.sh new file mode 100755 index 0000000000..1598630984 --- /dev/null +++ b/engine/src/flutter/testing/gtest_ios/run-unittest.sh @@ -0,0 +1,87 @@ +#!/bin/bash -p + +# Copyright (c) 2012 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. + +set -eu + +# Environment sanitization. Set a known-safe PATH. Clear environment variables +# that might impact the interpreter's operation. The |bash -p| invocation +# on the #! line takes the bite out of BASH_ENV, ENV, and SHELLOPTS (among +# other features), but clearing them here ensures that they won't impact any +# shell scripts used as utility programs. SHELLOPTS is read-only and can't be +# unset, only unexported. +export PATH="/usr/bin:/bin:/usr/sbin:/sbin" +unset BASH_ENV CDPATH ENV GLOBIGNORE IFS POSIXLY_CORRECT +export -n SHELLOPTS + +readonly ScriptDir=$(dirname "$(echo ${0} | sed -e "s,^\([^/]\),$(pwd)/\1,")") +readonly ScriptName=$(basename "${0}") +readonly ThisScript="${ScriptDir}/${ScriptName}" +readonly SimExecutable="${BUILD_DIR}/ninja-iossim/${CONFIGURATION}/iossim" + +# Helper to print a line formatted for Xcodes build output parser. +XcodeNote() { + echo "${ThisScript}:${1}: note: ${2}" +} + +# Helper to print a divider to make things stick out in a busy output window. +XcodeHeader() { + echo "note: _________________________________________________________________" + echo "note: _________________________________________________________________" + echo "note: _________________________________________________________________" + XcodeNote "$1" ">>>>> $2" + echo "note: _________________________________________________________________" + echo "note: _________________________________________________________________" + echo "note: _________________________________________________________________" +} + +# Kills the iPhone Simulator if it is running. +KillSimulator() { + /usr/bin/killall "iPhone Simulator" 2> /dev/null || true +} + +# Runs tests via the iPhone Simulator for multiple devices. +RunTests() { + local -r appPath="${TARGET_BUILD_DIR}/${PRODUCT_NAME}.app" + + if [[ ! -x "${SimExecutable}" ]]; then + echo "Unable to run tests: ${SimExecutable} was not found/executable." + exit 1 + fi + + for device in 'iPhone' 'iPad'; do + iosVersion="6.1" + KillSimulator + local command=( + "${SimExecutable}" "-d${device}" "-s${iosVersion}" "${appPath}" + ) + # Pass along any command line flags + if [[ "$#" -gt 0 ]]; then + command+=( "--" "${@}" ) + fi + XcodeHeader ${LINENO} "Launching tests for ${device} (iOS ${iosVersion})" + "${command[@]}" + + # If the command didn't exit successfully, abort. + if [[ $? -ne 0 ]]; then + exit $?; + fi + done +} + +# Time to get to work. + +if [[ "${PLATFORM_NAME}" != "iphonesimulator" ]]; then + XcodeNote ${LINENO} "Skipping running of unittests for device build." +else + if [[ "$#" -gt 0 ]]; then + RunTests "${@}" + else + RunTests + fi + KillSimulator +fi + +exit 0 diff --git a/engine/src/flutter/testing/gtest_ios/unittest-Info.plist b/engine/src/flutter/testing/gtest_ios/unittest-Info.plist new file mode 100644 index 0000000000..6b3c09453c --- /dev/null +++ b/engine/src/flutter/testing/gtest_ios/unittest-Info.plist @@ -0,0 +1,150 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.google.gtest.${BUNDLE_ID_TEST_NAME} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + NSMainNibFile + ${MAIN_NIB_FILE} + UILaunchImages + + + UILaunchImageMinimumOSVersion + 7.0 + UILaunchImageName + Default + UILaunchImageOrientation + Portrait + UILaunchImageSize + {320, 480} + + + UILaunchImageMinimumOSVersion + 7.0 + UILaunchImageName + Default + UILaunchImageOrientation + Portrait + UILaunchImageSize + {320, 568} + + + UILaunchImageMinimumOSVersion + 8.0 + UILaunchImageName + Default + UILaunchImageOrientation + Portrait + UILaunchImageSize + {375, 667} + + + UILaunchImageMinimumOSVersion + 8.0 + UILaunchImageName + Default + UILaunchImageOrientation + Portrait + UILaunchImageSize + {414, 736} + + + UILaunchImageMinimumOSVersion + 8.0 + UILaunchImageName + Default + UILaunchImageOrientation + Landscape + UILaunchImageSize + {414, 736} + + + UILaunchImages~ipad + + + UILaunchImageMinimumOSVersion + 7.0 + UILaunchImageName + Default + UILaunchImageOrientation + Portrait + UILaunchImageSize + {768, 1024} + + + UILaunchImageMinimumOSVersion + 7.0 + UILaunchImageName + Default + UILaunchImageOrientation + Landscape + UILaunchImageSize + {768, 1024} + + + UISupportedInterfaceOrientation + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleURLTypes + + + CFBundleURLSchemes + + com.google.sso.chrome + ${BUNDLE_ID_TEST_NAME}.http + ${BUNDLE_ID_TEST_NAME}.https + ${BUNDLE_ID_TEST_NAME}-x-callback + + + + UTImportedTypeDeclarations + + + UTTypeConformsTo + + public.url + org.appextension.find-login-action + + UTTypeDescription + Chrome Password Fill by App Extension Action + UTTypeIdentifier + org.appextension.chrome-password-action + + + UTTypeConformsTo + + public.url + + UTTypeDescription + 1Password Find Login Action + UTTypeIdentifier + org.appextension.find-login-action + + + UIBackgroundModes + + fetch + + + diff --git a/engine/src/flutter/testing/gtest_mac.h b/engine/src/flutter/testing/gtest_mac.h new file mode 100644 index 0000000000..aa48c94543 --- /dev/null +++ b/engine/src/flutter/testing/gtest_mac.h @@ -0,0 +1,48 @@ +// Copyright (c) 2010 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. + +#ifndef TESTING_GTEST_MAC_H_ +#define TESTING_GTEST_MAC_H_ + +#include +#include + +#ifdef GTEST_OS_MAC + +#import + +namespace testing { +namespace internal { + +// This overloaded version allows comparison between ObjC objects that conform +// to the NSObject protocol. Used to implement {ASSERT|EXPECT}_EQ(). +GTEST_API_ AssertionResult CmpHelperNSEQ(const char* expected_expression, + const char* actual_expression, + id expected, + id actual); + +// This overloaded version allows comparison between ObjC objects that conform +// to the NSObject protocol. Used to implement {ASSERT|EXPECT}_NE(). +GTEST_API_ AssertionResult CmpHelperNSNE(const char* expected_expression, + const char* actual_expression, + id expected, + id actual); + +} // namespace internal +} // namespace testing + +// Tests that [expected isEqual:actual]. +#define EXPECT_NSEQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNSEQ, expected, actual) +#define EXPECT_NSNE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNSNE, val1, val2) + +#define ASSERT_NSEQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNSEQ, expected, actual) +#define ASSERT_NSNE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNSNE, val1, val2) + +#endif // GTEST_OS_MAC + +#endif // TESTING_GTEST_MAC_H_ diff --git a/engine/src/flutter/testing/gtest_mac.mm b/engine/src/flutter/testing/gtest_mac.mm new file mode 100644 index 0000000000..6aee989013 --- /dev/null +++ b/engine/src/flutter/testing/gtest_mac.mm @@ -0,0 +1,61 @@ +// Copyright (c) 2010 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. + +#import "gtest_mac.h" + +#include + +#include +#include +#include + +#ifdef GTEST_OS_MAC + +#import + +namespace testing { +namespace internal { + +// Handles nil values for |obj| properly by using safe printing of %@ in +// -stringWithFormat:. +static inline const char* StringDescription(id obj) { + return [[NSString stringWithFormat:@"%@", obj] UTF8String]; +} + +// This overloaded version allows comparison between ObjC objects that conform +// to the NSObject protocol. Used to implement {ASSERT|EXPECT}_EQ(). +GTEST_API_ AssertionResult CmpHelperNSEQ(const char* expected_expression, + const char* actual_expression, + id expected, + id actual) { + if (expected == actual || [expected isEqual:actual]) { + return AssertionSuccess(); + } + return EqFailure(expected_expression, + actual_expression, + std::string(StringDescription(expected)), + std::string(StringDescription(actual)), + false); +} + +// This overloaded version allows comparison between ObjC objects that conform +// to the NSObject protocol. Used to implement {ASSERT|EXPECT}_NE(). +GTEST_API_ AssertionResult CmpHelperNSNE(const char* expected_expression, + const char* actual_expression, + id expected, + id actual) { + if (expected != actual && ![expected isEqual:actual]) { + return AssertionSuccess(); + } + Message msg; + msg << "Expected: (" << expected_expression << ") != (" << actual_expression + << "), actual: " << StringDescription(expected) + << " vs " << StringDescription(actual); + return AssertionFailure(msg); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_OS_MAC diff --git a/engine/src/flutter/testing/gtest_mac_unittest.mm b/engine/src/flutter/testing/gtest_mac_unittest.mm new file mode 100644 index 0000000000..01f14979fe --- /dev/null +++ b/engine/src/flutter/testing/gtest_mac_unittest.mm @@ -0,0 +1,57 @@ +// Copyright (c) 2012 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. + +// Note that while this file is in testing/ and tests GTest macros, it is built +// as part of Chromium's unit_tests target because the project does not build +// or run GTest's internal test suite. + +#import "testing/gtest_mac.h" + +#import + +#include "base/mac/scoped_nsautorelease_pool.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest/include/gtest/internal/gtest-port.h" + +TEST(GTestMac, ExpectNSEQ) { + base::mac::ScopedNSAutoreleasePool pool; + + EXPECT_NSEQ(@"a", @"a"); + + NSString* s1 = [NSString stringWithUTF8String:"a"]; + NSString* s2 = @"a"; + EXPECT_NE(s1, s2); + EXPECT_NSEQ(s1, s2); +} + +TEST(GTestMac, AssertNSEQ) { + base::mac::ScopedNSAutoreleasePool pool; + + NSString* s1 = [NSString stringWithUTF8String:"a"]; + NSString* s2 = @"a"; + EXPECT_NE(s1, s2); + ASSERT_NSEQ(s1, s2); +} + +TEST(GTestMac, ExpectNSNE) { + base::mac::ScopedNSAutoreleasePool pool; + + EXPECT_NSNE([NSNumber numberWithInt:2], [NSNumber numberWithInt:42]); +} + +TEST(GTestMac, AssertNSNE) { + base::mac::ScopedNSAutoreleasePool pool; + + ASSERT_NSNE(@"a", @"b"); +} + +TEST(GTestMac, ExpectNSNil) { + base::mac::ScopedNSAutoreleasePool pool; + + EXPECT_NSEQ(nil, nil); + EXPECT_NSNE(nil, @"a"); + EXPECT_NSNE(@"a", nil); + + // TODO(shess): Test that EXPECT_NSNE(nil, nil) fails. +} diff --git a/engine/src/flutter/testing/gtest_nacl.gyp b/engine/src/flutter/testing/gtest_nacl.gyp new file mode 100644 index 0000000000..d1c0ce0fb4 --- /dev/null +++ b/engine/src/flutter/testing/gtest_nacl.gyp @@ -0,0 +1,94 @@ +# Copyright 2014 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'includes': [ + '../build/common_untrusted.gypi', + 'gtest.gypi', + ], + 'conditions': [ + ['disable_nacl==0 and disable_nacl_untrusted==0', { + 'targets': [ + { + 'target_name': 'gtest_nacl', + 'type': 'none', + 'variables': { + 'nlib_target': 'libgtest_nacl.a', + 'build_glibc': 0, + 'build_newlib': 0, + 'build_irt': 0, + 'build_pnacl_newlib': 1, + 'build_nonsfi_helper': 1, + }, + 'sources': [ + '<@(gtest_sources)', + ], + 'include_dirs': [ + 'gtest', + 'gtest/include', + ], + 'defines': [ + # In order to allow regex matches in gtest to be shared between + # Windows and other systems, we tell gtest to always use it's + # internal engine. + 'GTEST_HAS_POSIX_RE=0', + 'GTEST_LANG_CXX11=0', + # gtest isn't able to figure out when RTTI is disabled for gcc + # versions older than 4.3.2, and assumes it's enabled. Our Mac + # and Linux builds disable RTTI, and cannot guarantee that the + # compiler will be 4.3.2. or newer. The Mac, for example, uses + # 4.2.1 as that is the latest available on that platform. gtest + # must be instructed that RTTI is disabled here, and for any + # direct dependents that might include gtest headers. + 'GTEST_HAS_RTTI=0', + ], + 'all_dependent_settings': { + 'defines': [ + 'GTEST_HAS_POSIX_RE=0', + 'GTEST_LANG_CXX11=0', + ], + 'link_flags': [ + '-lgtest_nacl', + ], + }, + 'direct_dependent_settings': { + 'defines': [ + 'UNIT_TEST', + 'GTEST_HAS_RTTI=0', + ], + 'include_dirs': [ + 'gtest/include', # So that gtest headers can find themselves. + ], + }, + }, + { + 'target_name': 'gtest_main_nacl', + 'type': 'none', + 'variables': { + 'nlib_target': 'libgtest_main_nacl.a', + 'build_glibc': 0, + 'build_newlib': 0, + 'build_irt': 0, + 'build_pnacl_newlib': 1, + 'build_nonsfi_helper': 1, + }, + 'dependencies': [ + 'gtest_nacl', + ], + 'sources': [ + 'gtest/src/gtest_main.cc', + ], + 'all_dependent_settings': { + 'link_flags': [ + '-lgtest_main_nacl', + ], + }, + }, + ], + }], + ], +} diff --git a/engine/src/flutter/testing/iossim/OWNERS b/engine/src/flutter/testing/iossim/OWNERS new file mode 100644 index 0000000000..1b3348e791 --- /dev/null +++ b/engine/src/flutter/testing/iossim/OWNERS @@ -0,0 +1,2 @@ +rohitrao@chromium.org +stuartmorgan@chromium.org diff --git a/engine/src/flutter/testing/iossim/iossim.gyp b/engine/src/flutter/testing/iossim/iossim.gyp new file mode 100644 index 0000000000..6041d396ab --- /dev/null +++ b/engine/src/flutter/testing/iossim/iossim.gyp @@ -0,0 +1,156 @@ +# Copyright (c) 2012 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. + +{ + 'variables': { + 'mac_deployment_target': '10.8' + }, + 'conditions': [ + ['OS!="ios" or "<(GENERATOR)"!="xcode" or "<(GENERATOR_FLAVOR)"=="ninja"', { + 'targets': [ + { + 'target_name': 'iossim', + 'toolsets': ['host'], + 'type': 'executable', + 'variables': { + 'developer_dir': '="6.0"', { + 'variables': { + 'iphone_sim_path': '<(developer_dir)/../SharedFrameworks', + }, + 'defines': [ + 'IOSSIM_USE_XCODE_6', + ], + 'actions': [ + { + 'action_name': 'generate_dvt_foundation_header', + 'inputs': [ + '<(iphone_sim_path)/DVTFoundation.framework/Versions/Current/DVTFoundation', + '<(PRODUCT_DIR)/class-dump', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/iossim/DVTFoundation.h' + ], + 'action': [ + # Actions don't provide a way to redirect stdout, so a custom + # script is invoked that will execute the first argument and + # write the output to the file specified as the second argument. + # -I sorts classes, categories, and protocols by inheritance. + # -C only displays classes matching regular expression. + './redirect-stdout.sh', + '<(PRODUCT_DIR)/class-dump -CDVTStackBacktrace|DVTInvalidation|DVTMixIn <(iphone_sim_path)/DVTFoundation.framework', + '<(INTERMEDIATE_DIR)/iossim/DVTFoundation.h', + ], + 'message': 'Generating DVTFoundation.h', + }, + { + 'action_name': 'generate_dvt_core_simulator', + 'inputs': [ + '<(developer_dir)/Library/PrivateFrameworks/CoreSimulator.framework/Versions/Current/CoreSimulator', + '<(PRODUCT_DIR)/class-dump', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/iossim/CoreSimulator.h' + ], + 'action': [ + # Actions don't provide a way to redirect stdout, so a custom + # script is invoked that will execute the first argument and + # write the output to the file specified as the second argument. + # -I sorts classes, categories, and protocols by inheritance. + # -C only displays classes matching regular expression. + './redirect-stdout.sh', + '<(PRODUCT_DIR)/class-dump -CSim <(developer_dir)/Library/PrivateFrameworks/CoreSimulator.framework', + '<(INTERMEDIATE_DIR)/iossim/CoreSimulator.h', + ], + 'message': 'Generating CoreSimulator.h', + }, + ], # actions + }, { # else: xcode_version<"6.0" + 'variables': { + 'iphone_sim_path': '<(developer_dir)/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks', + }, + }], # xcode_version + ], # conditions + 'dependencies': [ + '<(DEPTH)/third_party/class-dump/class-dump.gyp:class-dump#host', + ], + 'include_dirs': [ + '<(INTERMEDIATE_DIR)/iossim', + ], + 'sources': [ + 'iossim.mm', + '<(INTERMEDIATE_DIR)/iossim/iPhoneSimulatorRemoteClient.h', + ], + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Foundation.framework', + ], + 'actions': [ + { + 'action_name': 'generate_dvt_iphone_sim_header', + 'inputs': [ + '<(iphone_sim_path)/DVTiPhoneSimulatorRemoteClient.framework/Versions/Current/DVTiPhoneSimulatorRemoteClient', + '<(PRODUCT_DIR)/class-dump', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/iossim/DVTiPhoneSimulatorRemoteClient.h' + ], + 'action': [ + # Actions don't provide a way to redirect stdout, so a custom + # script is invoked that will execute the first argument and + # write the output to the file specified as the second argument. + # -I sorts classes, categories, and protocols by inheritance. + # -C only displays classes matching regular expression. + './redirect-stdout.sh', + '<(PRODUCT_DIR)/class-dump -I -CiPhoneSimulator <(iphone_sim_path)/DVTiPhoneSimulatorRemoteClient.framework', + '<(INTERMEDIATE_DIR)/iossim/DVTiPhoneSimulatorRemoteClient.h', + ], + 'message': 'Generating DVTiPhoneSimulatorRemoteClient.h', + }, + ], # actions + 'xcode_settings': { + 'ARCHS': ['x86_64'], + }, + }, + ], + }, { # else, OS=="ios" and "<(GENERATOR)"=="xcode" and "<(GENERATOR_FLAVOR)"!="ninja" + 'variables': { + 'ninja_output_dir': 'ninja-iossim', + 'ninja_product_dir': + '$(SYMROOT)/<(ninja_output_dir)/<(CONFIGURATION_NAME)', + }, + 'targets': [ + { + 'target_name': 'iossim', + 'type': 'none', + 'toolsets': ['host'], + 'variables': { + # Gyp to rerun + 're_run_targets': [ + 'testing/iossim/iossim.gyp', + ], + }, + 'includes': ['../../build/ios/mac_build.gypi'], + 'actions': [ + { + 'action_name': 'compile iossim', + 'inputs': [], + 'outputs': [], + 'action': [ + '<@(ninja_cmd)', + 'iossim', + ], + 'message': 'Generating the iossim executable', + }, + ], + }, + ], + }], + ], +} diff --git a/engine/src/flutter/testing/iossim/iossim.mm b/engine/src/flutter/testing/iossim/iossim.mm new file mode 100644 index 0000000000..3910ba5d60 --- /dev/null +++ b/engine/src/flutter/testing/iossim/iossim.mm @@ -0,0 +1,1025 @@ +// Copyright (c) 2012 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. + +#include +#import +#include +#include +#include + +// An executable (iossim) that runs an app in the iOS Simulator. +// Run 'iossim -h' for usage information. +// +// For best results, the iOS Simulator application should not be running when +// iossim is invoked. +// +// Headers for iPhoneSimulatorRemoteClient and other frameworks used in this +// tool are generated by class-dump, via GYP. +// (class-dump is available at http://www.codethecode.com/projects/class-dump/) +// +// However, there are some forward declarations required to get things to +// compile. + +// TODO(lliabraa): Once all builders are on Xcode 6 this ifdef can be removed +// (crbug.com/385030). +#if defined(IOSSIM_USE_XCODE_6) +@class DVTStackBacktrace; +#import "DVTFoundation.h" +#endif // IOSSIM_USE_XCODE_6 + +// TODO(lliabraa): Once all builders are on Xcode 6 this ifdef can be removed +// (crbug.com/385030). +#if defined(IOSSIM_USE_XCODE_6) +@protocol SimBridge; +@class SimDeviceSet; +@class SimDeviceType; +@class SimRuntime; +@class SimServiceConnectionManager; +#import "CoreSimulator.h" +#endif // IOSSIM_USE_XCODE_6 + +@interface DVTPlatform : NSObject ++ (BOOL)loadAllPlatformsReturningError:(id*)arg1; +@end +@class DTiPhoneSimulatorApplicationSpecifier; +@class DTiPhoneSimulatorSession; +@class DTiPhoneSimulatorSessionConfig; +@class DTiPhoneSimulatorSystemRoot; +@class DVTConfinementServiceConnection; +@class DVTDispatchLock; +@class DVTiPhoneSimulatorMessenger; +@class DVTNotificationToken; +@class DVTTask; +// The DTiPhoneSimulatorSessionDelegate protocol is referenced +// by the iPhoneSimulatorRemoteClient framework, but not defined in the object +// file, so it must be defined here before importing the generated +// iPhoneSimulatorRemoteClient.h file. +@protocol DTiPhoneSimulatorSessionDelegate +- (void)session:(DTiPhoneSimulatorSession*)session + didEndWithError:(NSError*)error; +- (void)session:(DTiPhoneSimulatorSession*)session + didStart:(BOOL)started + withError:(NSError*)error; +@end +#import "DVTiPhoneSimulatorRemoteClient.h" + +// An undocumented system log key included in messages from launchd. The value +// is the PID of the process the message is about (as opposed to launchd's PID). +#define ASL_KEY_REF_PID "RefPID" + +namespace { + +// Name of environment variables that control the user's home directory in the +// simulator. +const char* const kUserHomeEnvVariable = "CFFIXED_USER_HOME"; +const char* const kHomeEnvVariable = "HOME"; + +// Device family codes for iPhone and iPad. +const int kIPhoneFamily = 1; +const int kIPadFamily = 2; + +// Max number of seconds to wait for the simulator session to start. +// This timeout must allow time to start up iOS Simulator, install the app +// and perform any other black magic that is encoded in the +// iPhoneSimulatorRemoteClient framework to kick things off. Normal start up +// time is only a couple seconds but machine load, disk caches, etc., can all +// affect startup time in the wild so the timeout needs to be fairly generous. +// If this timeout occurs iossim will likely exit with non-zero status; the +// exception being if the app is invoked and completes execution before the +// session is started (this case is handled in session:didStart:withError). +const NSTimeInterval kDefaultSessionStartTimeoutSeconds = 30; + +// While the simulated app is running, its stdout is redirected to a file which +// is polled by iossim and written to iossim's stdout using the following +// polling interval. +const NSTimeInterval kOutputPollIntervalSeconds = 0.1; + +NSString* const kDVTFoundationRelativePath = + @"../SharedFrameworks/DVTFoundation.framework"; +NSString* const kDevToolsFoundationRelativePath = + @"../OtherFrameworks/DevToolsFoundation.framework"; +NSString* const kSimulatorRelativePath = + @"Platforms/iPhoneSimulator.platform/Developer/Applications/" + @"iPhone Simulator.app"; + +// Simulator Error String Key. This can be found by looking in the Simulator's +// Localizable.strings files. +NSString* const kSimulatorAppQuitErrorKey = @"The simulated application quit."; + +const char* gToolName = "iossim"; + +// Exit status codes. +const int kExitSuccess = EXIT_SUCCESS; +const int kExitFailure = EXIT_FAILURE; +const int kExitInvalidArguments = 2; +const int kExitInitializationFailure = 3; +const int kExitAppFailedToStart = 4; +const int kExitAppCrashed = 5; +const int kExitUnsupportedXcodeVersion = 6; + +void LogError(NSString* format, ...) { + va_list list; + va_start(list, format); + + NSString* message = + [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; + + fprintf(stderr, "%s: ERROR: %s\n", gToolName, [message UTF8String]); + fflush(stderr); + + va_end(list); +} + +void LogWarning(NSString* format, ...) { + va_list list; + va_start(list, format); + + NSString* message = + [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; + + fprintf(stderr, "%s: WARNING: %s\n", gToolName, [message UTF8String]); + fflush(stderr); + + va_end(list); +} + +// Helper to find a class by name and die if it isn't found. +Class FindClassByName(NSString* nameOfClass) { + Class theClass = NSClassFromString(nameOfClass); + if (!theClass) { + LogError(@"Failed to find class %@ at runtime.", nameOfClass); + exit(kExitInitializationFailure); + } + return theClass; +} + +// Returns the a NSString containing the stdout from running an NSTask that +// launches |toolPath| with th given command line |args|. +NSString* GetOutputFromTask(NSString* toolPath, NSArray* args) { + NSTask* task = [[[NSTask alloc] init] autorelease]; + [task setLaunchPath:toolPath]; + [task setArguments:args]; + NSPipe* outputPipe = [NSPipe pipe]; + [task setStandardOutput:outputPipe]; + NSFileHandle* outputFile = [outputPipe fileHandleForReading]; + + [task launch]; + NSData* outputData = [outputFile readDataToEndOfFile]; + [task waitUntilExit]; + if ([task isRunning]) { + LogError(@"Task '%@ %@' is still running.", + toolPath, + [args componentsJoinedByString:@" "]); + return nil; + } else if ([task terminationStatus]) { + LogError(@"Task '%@ %@' exited with return code %d.", + toolPath, + [args componentsJoinedByString:@" "], + [task terminationStatus]); + return nil; + } + return [[[NSString alloc] initWithData:outputData + encoding:NSUTF8StringEncoding] autorelease]; +} + +// Finds the Xcode version via xcodebuild -version. Output from xcodebuild is +// expected to look like: +// Xcode +// Build version 5B130a +// where is the string returned by this function (e.g. 6.0). +NSString* FindXcodeVersion() { + NSString* output = GetOutputFromTask(@"/usr/bin/xcodebuild", + @[ @"-version" ]); + // Scan past the "Xcode ", then scan the rest of the line into |version|. + NSScanner* scanner = [NSScanner scannerWithString:output]; + BOOL valid = [scanner scanString:@"Xcode " intoString:NULL]; + NSString* version; + valid = + [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] + intoString:&version]; + if (!valid) { + LogError(@"Unable to find Xcode version. 'xcodebuild -version' " + @"returned \n%@", output); + return nil; + } + return version; +} + +// Returns true if iossim is running with Xcode 6 or later installed on the +// host. +BOOL IsRunningWithXcode6OrLater() { + static NSString* xcodeVersion = FindXcodeVersion(); + if (!xcodeVersion) { + return false; + } + NSArray* components = [xcodeVersion componentsSeparatedByString:@"."]; + if ([components count] < 1) { + return false; + } + NSInteger majorVersion = [[components objectAtIndex:0] integerValue]; + return majorVersion >= 6; +} + +// Prints supported devices and SDKs. +void PrintSupportedDevices() { + if (IsRunningWithXcode6OrLater()) { +#if defined(IOSSIM_USE_XCODE_6) + printf("Supported device/SDK combinations:\n"); + Class simDeviceSetClass = FindClassByName(@"SimDeviceSet"); + id deviceSet = + [simDeviceSetClass setForSetPath:[simDeviceSetClass defaultSetPath]]; + for (id simDevice in [deviceSet availableDevices]) { + NSString* deviceInfo = + [NSString stringWithFormat:@" -d '%@' -s '%@'\n", + [simDevice name], [[simDevice runtime] versionString]]; + printf("%s", [deviceInfo UTF8String]); + } +#endif // IOSSIM_USE_XCODE_6 + } else { + printf("Supported SDK versions:\n"); + Class rootClass = FindClassByName(@"DTiPhoneSimulatorSystemRoot"); + for (id root in [rootClass knownRoots]) { + printf(" '%s'\n", [[root sdkVersion] UTF8String]); + } + // This is the list of devices supported on Xcode 5.1.x. + printf("Supported devices:\n"); + printf(" 'iPhone'\n"); + printf(" 'iPhone Retina (3.5-inch)'\n"); + printf(" 'iPhone Retina (4-inch)'\n"); + printf(" 'iPhone Retina (4-inch 64-bit)'\n"); + printf(" 'iPad'\n"); + printf(" 'iPad Retina'\n"); + printf(" 'iPad Retina (64-bit)'\n"); + } +} +} // namespace + +// A delegate that is called when the simulated app is started or ended in the +// simulator. +@interface SimulatorDelegate : NSObject { + @private + NSString* stdioPath_; + NSString* developerDir_; + NSString* simulatorHome_; + NSThread* outputThread_; + NSBundle* simulatorBundle_; + BOOL appRunning_; +} +@end + +// An implementation that copies the simulated app's stdio to stdout of this +// executable. While it would be nice to get stdout and stderr independently +// from iOS Simulator, issues like I/O buffering and interleaved output +// between iOS Simulator and the app would cause iossim to display things out +// of order here. Printing all output to a single file keeps the order correct. +// Instances of this classe should be initialized with the location of the +// simulated app's output file. When the simulated app starts, a thread is +// started which handles copying data from the simulated app's output file to +// the stdout of this executable. +@implementation SimulatorDelegate + +// Specifies the file locations of the simulated app's stdout and stderr. +- (SimulatorDelegate*)initWithStdioPath:(NSString*)stdioPath + developerDir:(NSString*)developerDir + simulatorHome:(NSString*)simulatorHome { + self = [super init]; + if (self) { + stdioPath_ = [stdioPath copy]; + developerDir_ = [developerDir copy]; + simulatorHome_ = [simulatorHome copy]; + } + + return self; +} + +- (void)dealloc { + [stdioPath_ release]; + [developerDir_ release]; + [simulatorBundle_ release]; + [super dealloc]; +} + +// Reads data from the simulated app's output and writes it to stdout. This +// method blocks, so it should be called in a separate thread. The iOS +// Simulator takes a file path for the simulated app's stdout and stderr, but +// this path isn't always available (e.g. when the stdout is Xcode's build +// window). As a workaround, iossim creates a temp file to hold output, which +// this method reads and copies to stdout. +- (void)tailOutputForSession:(DTiPhoneSimulatorSession*)session { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSFileHandle* simio = [NSFileHandle fileHandleForReadingAtPath:stdioPath_]; + if (IsRunningWithXcode6OrLater()) { +#if defined(IOSSIM_USE_XCODE_6) + // With iOS 8 simulators on Xcode 6, the app output is relative to the + // simulator's data directory. + NSString* versionString = + [[[session sessionConfig] simulatedSystemRoot] sdkVersion]; + NSInteger majorVersion = [[[versionString componentsSeparatedByString:@"."] + objectAtIndex:0] intValue]; + if (majorVersion >= 8) { + NSString* dataPath = session.sessionConfig.device.dataPath; + NSString* appOutput = + [dataPath stringByAppendingPathComponent:stdioPath_]; + simio = [NSFileHandle fileHandleForReadingAtPath:appOutput]; + } +#endif // IOSSIM_USE_XCODE_6 + } + NSFileHandle* standardOutput = [NSFileHandle fileHandleWithStandardOutput]; + // Copy data to stdout/stderr while the app is running. + while (appRunning_) { + NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init]; + [standardOutput writeData:[simio readDataToEndOfFile]]; + [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds]; + [innerPool drain]; + } + + // Once the app is no longer running, copy any data that was written during + // the last sleep cycle. + [standardOutput writeData:[simio readDataToEndOfFile]]; + + [pool drain]; +} + +// Fetches a localized error string from the Simulator. +- (NSString *)localizedSimulatorErrorString:(NSString*)stringKey { + // Lazy load of the simulator bundle. + if (simulatorBundle_ == nil) { + NSString* simulatorPath = [developerDir_ + stringByAppendingPathComponent:kSimulatorRelativePath]; + simulatorBundle_ = [NSBundle bundleWithPath:simulatorPath]; + } + NSString *localizedStr = + [simulatorBundle_ localizedStringForKey:stringKey + value:nil + table:nil]; + if ([localizedStr length]) + return localizedStr; + // Failed to get a value, follow Cocoa conventions and use the key as the + // string. + return stringKey; +} + +- (void)session:(DTiPhoneSimulatorSession*)session + didStart:(BOOL)started + withError:(NSError*)error { + if (!started) { + // If the test executes very quickly (<30ms), the SimulatorDelegate may not + // get the initial session:started:withError: message indicating successful + // startup of the simulated app. Instead the delegate will get a + // session:started:withError: message after the timeout has elapsed. To + // account for this case, check if the simulated app's stdio file was + // ever created and if it exists dump it to stdout and return success. + NSFileManager* fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:stdioPath_]) { + appRunning_ = NO; + [self tailOutputForSession:session]; + // Note that exiting in this state leaves a process running + // (e.g. /.../iPhoneSimulator4.3.sdk/usr/libexec/installd -t 30) that will + // prevent future simulator sessions from being started for 30 seconds + // unless the iOS Simulator application is killed altogether. + [self session:session didEndWithError:nil]; + + // session:didEndWithError should not return (because it exits) so + // the execution path should never get here. + exit(kExitFailure); + } + + LogError(@"Simulator failed to start: \"%@\" (%@:%ld)", + [error localizedDescription], + [error domain], static_cast([error code])); + PrintSupportedDevices(); + exit(kExitAppFailedToStart); + } + + // Start a thread to write contents of outputPath to stdout. + appRunning_ = YES; + outputThread_ = + [[NSThread alloc] initWithTarget:self + selector:@selector(tailOutputForSession:) + object:session]; + [outputThread_ start]; +} + +- (void)session:(DTiPhoneSimulatorSession*)session + didEndWithError:(NSError*)error { + appRunning_ = NO; + // Wait for the output thread to finish copying data to stdout. + if (outputThread_) { + while (![outputThread_ isFinished]) { + [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds]; + } + [outputThread_ release]; + outputThread_ = nil; + } + + if (error) { + // There appears to be a race condition where sometimes the simulator + // framework will end with an error, but the error is that the simulated + // app cleanly shut down; try to trap this error and don't fail the + // simulator run. + NSString* localizedDescription = [error localizedDescription]; + NSString* ignorableErrorStr = + [self localizedSimulatorErrorString:kSimulatorAppQuitErrorKey]; + if ([ignorableErrorStr isEqual:localizedDescription]) { + LogWarning(@"Ignoring that Simulator ended with: \"%@\" (%@:%ld)", + localizedDescription, [error domain], + static_cast([error code])); + } else { + LogError(@"Simulator ended with error: \"%@\" (%@:%ld)", + localizedDescription, [error domain], + static_cast([error code])); + exit(kExitFailure); + } + } + + // Try to determine if the simulated app crashed or quit with a non-zero + // status code. iOS Simluator handles things a bit differently depending on + // the version, so first determine the iOS version being used. + BOOL badEntryFound = NO; + NSString* versionString = + [[[session sessionConfig] simulatedSystemRoot] sdkVersion]; + NSInteger majorVersion = [[[versionString componentsSeparatedByString:@"."] + objectAtIndex:0] intValue]; + if (majorVersion <= 6) { + // In iOS 6 and before, logging from the simulated apps went to the main + // system logs, so use ASL to check if the simulated app exited abnormally + // by looking for system log messages from launchd that refer to the + // simulated app's PID. Limit query to messages in the last minute since + // PIDs are cyclical. + aslmsg query = asl_new(ASL_TYPE_QUERY); + asl_set_query(query, ASL_KEY_SENDER, "launchd", + ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING); + char session_id[20]; + if (snprintf(session_id, 20, "%d", [session simulatedApplicationPID]) < 0) { + LogError(@"Failed to get [session simulatedApplicationPID]"); + exit(kExitFailure); + } + asl_set_query(query, ASL_KEY_REF_PID, session_id, ASL_QUERY_OP_EQUAL); + asl_set_query(query, ASL_KEY_TIME, "-1m", ASL_QUERY_OP_GREATER_EQUAL); + + // Log any messages found, and take note of any messages that may indicate + // the app crashed or did not exit cleanly. + aslresponse response = asl_search(NULL, query); + aslmsg entry; + while ((entry = aslresponse_next(response)) != NULL) { + const char* message = asl_get(entry, ASL_KEY_MSG); + LogWarning(@"Console message: %s", message); + // Some messages are harmless, so don't trigger a failure for them. + if (strstr(message, "The following job tried to hijack the service")) + continue; + badEntryFound = YES; + } + } else { + // Otherwise, the iOS Simulator's system logging is sandboxed, so parse the + // sandboxed system.log file for known errors. + NSString* path; + if (IsRunningWithXcode6OrLater()) { +#if defined(IOSSIM_USE_XCODE_6) + NSString* dataPath = session.sessionConfig.device.dataPath; + path = + [dataPath stringByAppendingPathComponent:@"Library/Logs/system.log"]; +#endif // IOSSIM_USE_XCODE_6 + } else { + NSString* relativePathToSystemLog = + [NSString stringWithFormat: + @"Library/Logs/iOS Simulator/%@/system.log", versionString]; + path = [simulatorHome_ + stringByAppendingPathComponent:relativePathToSystemLog]; + } + NSFileManager* fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:path]) { + NSString* content = + [NSString stringWithContentsOfFile:path + encoding:NSUTF8StringEncoding + error:NULL]; + NSArray* lines = [content componentsSeparatedByCharactersInSet: + [NSCharacterSet newlineCharacterSet]]; + NSString* simulatedAppPID = + [NSString stringWithFormat:@"%d", session.simulatedApplicationPID]; + NSArray* kErrorStrings = @[ + @"Service exited with abnormal code:", + @"Service exited due to signal:", + ]; + for (NSString* line in lines) { + if ([line rangeOfString:simulatedAppPID].location != NSNotFound) { + for (NSString* errorString in kErrorStrings) { + if ([line rangeOfString:errorString].location != NSNotFound) { + LogWarning(@"Console message: %@", line); + badEntryFound = YES; + break; + } + } + if (badEntryFound) { + break; + } + } + } + // Remove the log file so subsequent invocations of iossim won't be + // looking at stale logs. + remove([path fileSystemRepresentation]); + } else { + LogWarning(@"Unable to find system log at '%@'.", path); + } + } + + // If the query returned any nasty-looking results, iossim should exit with + // non-zero status. + if (badEntryFound) { + LogError(@"Simulated app crashed or exited with non-zero status"); + exit(kExitAppCrashed); + } + exit(kExitSuccess); +} +@end + +namespace { + +// Finds the developer dir via xcode-select or the DEVELOPER_DIR environment +// variable. +NSString* FindDeveloperDir() { + // Check the env first. + NSDictionary* env = [[NSProcessInfo processInfo] environment]; + NSString* developerDir = [env objectForKey:@"DEVELOPER_DIR"]; + if ([developerDir length] > 0) + return developerDir; + + // Go look for it via xcode-select. + NSString* output = GetOutputFromTask(@"/usr/bin/xcode-select", + @[ @"-print-path" ]); + output = [output stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ([output length] == 0) + output = nil; + return output; +} + +// Loads the Simulator framework from the given developer dir. +NSBundle* LoadSimulatorFramework(NSString* developerDir) { + // The Simulator framework depends on some of the other Xcode private + // frameworks; manually load them first so everything can be linked up. + NSString* dvtFoundationPath = [developerDir + stringByAppendingPathComponent:kDVTFoundationRelativePath]; + NSBundle* dvtFoundationBundle = + [NSBundle bundleWithPath:dvtFoundationPath]; + if (![dvtFoundationBundle load]) + return nil; + + NSString* devToolsFoundationPath = [developerDir + stringByAppendingPathComponent:kDevToolsFoundationRelativePath]; + NSBundle* devToolsFoundationBundle = + [NSBundle bundleWithPath:devToolsFoundationPath]; + if (![devToolsFoundationBundle load]) + return nil; + + // Prime DVTPlatform. + NSError* error; + Class DVTPlatformClass = FindClassByName(@"DVTPlatform"); + if (![DVTPlatformClass loadAllPlatformsReturningError:&error]) { + LogError(@"Unable to loadAllPlatformsReturningError. Error: %@", + [error localizedDescription]); + return nil; + } + + // The path within the developer dir of the private Simulator frameworks. + NSString* simulatorFrameworkRelativePath; + if (IsRunningWithXcode6OrLater()) { + simulatorFrameworkRelativePath = + @"../SharedFrameworks/DVTiPhoneSimulatorRemoteClient.framework"; + NSString* const kCoreSimulatorRelativePath = + @"Library/PrivateFrameworks/CoreSimulator.framework"; + NSString* coreSimulatorPath = [developerDir + stringByAppendingPathComponent:kCoreSimulatorRelativePath]; + NSBundle* coreSimulatorBundle = + [NSBundle bundleWithPath:coreSimulatorPath]; + if (![coreSimulatorBundle load]) + return nil; + } else { + simulatorFrameworkRelativePath = + @"Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/" + @"DVTiPhoneSimulatorRemoteClient.framework"; + } + NSString* simBundlePath = [developerDir + stringByAppendingPathComponent:simulatorFrameworkRelativePath]; + NSBundle* simBundle = [NSBundle bundleWithPath:simBundlePath]; + if (![simBundle load]) + return nil; + return simBundle; +} + +// Converts the given app path to an application spec, which requires an +// absolute path. +DTiPhoneSimulatorApplicationSpecifier* BuildAppSpec(NSString* appPath) { + Class applicationSpecifierClass = + FindClassByName(@"DTiPhoneSimulatorApplicationSpecifier"); + if (![appPath isAbsolutePath]) { + NSString* cwd = [[NSFileManager defaultManager] currentDirectoryPath]; + appPath = [cwd stringByAppendingPathComponent:appPath]; + } + appPath = [appPath stringByStandardizingPath]; + NSFileManager* fileManager = [NSFileManager defaultManager]; + if (![fileManager fileExistsAtPath:appPath]) { + LogError(@"File not found: %@", appPath); + exit(kExitInvalidArguments); + } + return [applicationSpecifierClass specifierWithApplicationPath:appPath]; +} + +// Returns the system root for the given SDK version. If sdkVersion is nil, the +// default system root is returned. Will return nil if the sdkVersion is not +// valid. +DTiPhoneSimulatorSystemRoot* BuildSystemRoot(NSString* sdkVersion) { + Class systemRootClass = FindClassByName(@"DTiPhoneSimulatorSystemRoot"); + DTiPhoneSimulatorSystemRoot* systemRoot = [systemRootClass defaultRoot]; + if (sdkVersion) + systemRoot = [systemRootClass rootWithSDKVersion:sdkVersion]; + + return systemRoot; +} + +// Builds a config object for starting the specified app. +DTiPhoneSimulatorSessionConfig* BuildSessionConfig( + DTiPhoneSimulatorApplicationSpecifier* appSpec, + DTiPhoneSimulatorSystemRoot* systemRoot, + NSString* stdoutPath, + NSString* stderrPath, + NSArray* appArgs, + NSDictionary* appEnv, + NSNumber* deviceFamily, + NSString* deviceName) { + Class sessionConfigClass = FindClassByName(@"DTiPhoneSimulatorSessionConfig"); + DTiPhoneSimulatorSessionConfig* sessionConfig = + [[[sessionConfigClass alloc] init] autorelease]; + sessionConfig.applicationToSimulateOnStart = appSpec; + sessionConfig.simulatedSystemRoot = systemRoot; + sessionConfig.localizedClientName = @"chromium"; + sessionConfig.simulatedApplicationStdErrPath = stderrPath; + sessionConfig.simulatedApplicationStdOutPath = stdoutPath; + sessionConfig.simulatedApplicationLaunchArgs = appArgs; + sessionConfig.simulatedApplicationLaunchEnvironment = appEnv; + sessionConfig.simulatedDeviceInfoName = deviceName; + sessionConfig.simulatedDeviceFamily = deviceFamily; + + if (IsRunningWithXcode6OrLater()) { +#if defined(IOSSIM_USE_XCODE_6) + Class simDeviceTypeClass = FindClassByName(@"SimDeviceType"); + id simDeviceType = + [simDeviceTypeClass supportedDeviceTypesByName][deviceName]; + Class simRuntimeClass = FindClassByName(@"SimRuntime"); + NSString* identifier = systemRoot.runtime.identifier; + id simRuntime = [simRuntimeClass supportedRuntimesByIdentifier][identifier]; + + // Attempt to use an existing device, but create one if a suitable match + // can't be found. For example, if the simulator is running with a + // non-default home directory (e.g. via iossim's -u command line arg) then + // there won't be any devices so one will have to be created. + Class simDeviceSetClass = FindClassByName(@"SimDeviceSet"); + id deviceSet = + [simDeviceSetClass setForSetPath:[simDeviceSetClass defaultSetPath]]; + id simDevice = nil; + for (id device in [deviceSet availableDevices]) { + if ([device runtime] == simRuntime && + [device deviceType] == simDeviceType) { + simDevice = device; + break; + } + } + if (!simDevice) { + NSError* error = nil; + // n.b. only the device name is necessary because the iOS Simulator menu + // already splits devices by runtime version. + NSString* name = [NSString stringWithFormat:@"iossim - %@ ", deviceName]; + simDevice = [deviceSet createDeviceWithType:simDeviceType + runtime:simRuntime + name:name + error:&error]; + if (error) { + LogError(@"Failed to create device: %@", error); + exit(kExitInitializationFailure); + } + } + sessionConfig.device = simDevice; +#endif // IOSSIM_USE_XCODE_6 + } + return sessionConfig; +} + +// Builds a simulator session that will use the given delegate. +DTiPhoneSimulatorSession* BuildSession(SimulatorDelegate* delegate) { + Class sessionClass = FindClassByName(@"DTiPhoneSimulatorSession"); + DTiPhoneSimulatorSession* session = + [[[sessionClass alloc] init] autorelease]; + session.delegate = delegate; + return session; +} + +// Creates a temporary directory with a unique name based on the provided +// template. The template should not contain any path separators and be suffixed +// with X's, which will be substituted with a unique alphanumeric string (see +// 'man mkdtemp' for details). The directory will be created as a subdirectory +// of NSTemporaryDirectory(). For example, if dirNameTemplate is 'test-XXX', +// this method would return something like '/path/to/tempdir/test-3n2'. +// +// Returns the absolute path of the newly-created directory, or nill if unable +// to create a unique directory. +NSString* CreateTempDirectory(NSString* dirNameTemplate) { + NSString* fullPathTemplate = + [NSTemporaryDirectory() stringByAppendingPathComponent:dirNameTemplate]; + char* fullPath = mkdtemp(const_cast([fullPathTemplate UTF8String])); + if (fullPath == NULL) + return nil; + + return [NSString stringWithUTF8String:fullPath]; +} + +// Creates the necessary directory structure under the given user home directory +// path. +// Returns YES if successful, NO if unable to create the directories. +BOOL CreateHomeDirSubDirs(NSString* userHomePath) { + NSFileManager* fileManager = [NSFileManager defaultManager]; + + // Create user home and subdirectories. + NSArray* subDirsToCreate = [NSArray arrayWithObjects: + @"Documents", + @"Library/Caches", + @"Library/Preferences", + nil]; + for (NSString* subDir in subDirsToCreate) { + NSString* path = [userHomePath stringByAppendingPathComponent:subDir]; + NSError* error; + if (![fileManager createDirectoryAtPath:path + withIntermediateDirectories:YES + attributes:nil + error:&error]) { + LogError(@"Unable to create directory: %@. Error: %@", + path, [error localizedDescription]); + return NO; + } + } + + return YES; +} + +// Creates the necessary directory structure under the given user home directory +// path, then sets the path in the appropriate environment variable. +// Returns YES if successful, NO if unable to create or initialize the given +// directory. +BOOL InitializeSimulatorUserHome(NSString* userHomePath) { + if (!CreateHomeDirSubDirs(userHomePath)) + return NO; + + // Update the environment to use the specified directory as the user home + // directory. + // Note: the third param of setenv specifies whether or not to overwrite the + // variable's value if it has already been set. + if ((setenv(kUserHomeEnvVariable, [userHomePath UTF8String], YES) == -1) || + (setenv(kHomeEnvVariable, [userHomePath UTF8String], YES) == -1)) { + LogError(@"Unable to set environment variables for home directory."); + return NO; + } + + return YES; +} + +// Performs a case-insensitive search to see if |stringToSearch| begins with +// |prefixToFind|. Returns true if a match is found. +BOOL CaseInsensitivePrefixSearch(NSString* stringToSearch, + NSString* prefixToFind) { + NSStringCompareOptions options = (NSAnchoredSearch | NSCaseInsensitiveSearch); + NSRange range = [stringToSearch rangeOfString:prefixToFind + options:options]; + return range.location != NSNotFound; +} + +// Prints the usage information to stderr. +void PrintUsage() { + fprintf(stderr, "Usage: iossim [-d device] [-s sdkVersion] [-u homeDir] " + "[-e envKey=value]* [-t startupTimeout] []\n" + " where is the path to the .app directory and appArgs are any" + " arguments to send the simulated app.\n" + "\n" + "Options:\n" + " -d Specifies the device (must be one of the values from the iOS" + " Simulator's Hardware -> Device menu. Defaults to 'iPhone'.\n" + " -s Specifies the SDK version to use (e.g '4.3')." + " Will use system default if not specified.\n" + " -u Specifies a user home directory for the simulator." + " Will create a new directory if not specified.\n" + " -e Specifies an environment key=value pair that will be" + " set in the simulated application's environment.\n" + " -t Specifies the session startup timeout (in seconds)." + " Defaults to %d.\n" + " -l List supported devices and iOS versions.\n", + static_cast(kDefaultSessionStartTimeoutSeconds)); +} +} // namespace + +void EnsureSupportForCurrentXcodeVersion() { + if (IsRunningWithXcode6OrLater()) { +#if !IOSSIM_USE_XCODE_6 + LogError(@"Running on Xcode 6, but Xcode 6 support was not compiled in."); + exit(kExitUnsupportedXcodeVersion); +#endif // IOSSIM_USE_XCODE_6 + } +} + +int main(int argc, char* const argv[]) { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + EnsureSupportForCurrentXcodeVersion(); + + // basename() may modify the passed in string and it returns a pointer to an + // internal buffer. Give it a copy to modify, and copy what it returns. + char* worker = strdup(argv[0]); + char* toolName = basename(worker); + if (toolName != NULL) { + toolName = strdup(toolName); + if (toolName != NULL) + gToolName = toolName; + } + if (worker != NULL) + free(worker); + + NSString* appPath = nil; + NSString* appName = nil; + NSString* sdkVersion = nil; + NSString* deviceName = + IsRunningWithXcode6OrLater() ? @"iPhone 5s" : @"iPhone"; + NSString* simHomePath = nil; + NSMutableArray* appArgs = [NSMutableArray array]; + NSMutableDictionary* appEnv = [NSMutableDictionary dictionary]; + NSTimeInterval sessionStartTimeout = kDefaultSessionStartTimeoutSeconds; + + NSString* developerDir = FindDeveloperDir(); + if (!developerDir) { + LogError(@"Unable to find developer directory."); + exit(kExitInitializationFailure); + } + + NSBundle* simulatorFramework = LoadSimulatorFramework(developerDir); + if (!simulatorFramework) { + LogError(@"Failed to load the Simulator Framework."); + exit(kExitInitializationFailure); + } + + // Parse the optional arguments + int c; + while ((c = getopt(argc, argv, "hs:d:u:e:t:l")) != -1) { + switch (c) { + case 's': + sdkVersion = [NSString stringWithUTF8String:optarg]; + break; + case 'd': + deviceName = [NSString stringWithUTF8String:optarg]; + break; + case 'u': + simHomePath = [[NSFileManager defaultManager] + stringWithFileSystemRepresentation:optarg length:strlen(optarg)]; + break; + case 'e': { + NSString* envLine = [NSString stringWithUTF8String:optarg]; + NSRange range = [envLine rangeOfString:@"="]; + if (range.location == NSNotFound) { + LogError(@"Invalid key=value argument for -e."); + PrintUsage(); + exit(kExitInvalidArguments); + } + NSString* key = [envLine substringToIndex:range.location]; + NSString* value = [envLine substringFromIndex:(range.location + 1)]; + [appEnv setObject:value forKey:key]; + } + break; + case 't': { + int timeout = atoi(optarg); + if (timeout > 0) { + sessionStartTimeout = static_cast(timeout); + } else { + LogError(@"Invalid startup timeout (%s).", optarg); + PrintUsage(); + exit(kExitInvalidArguments); + } + } + break; + case 'l': + PrintSupportedDevices(); + exit(kExitSuccess); + break; + case 'h': + PrintUsage(); + exit(kExitSuccess); + default: + PrintUsage(); + exit(kExitInvalidArguments); + } + } + + // There should be at least one arg left, specifying the app path. Any + // additional args are passed as arguments to the app. + if (optind < argc) { + appPath = [[NSFileManager defaultManager] + stringWithFileSystemRepresentation:argv[optind] + length:strlen(argv[optind])]; + appName = [appPath lastPathComponent]; + while (++optind < argc) { + [appArgs addObject:[NSString stringWithUTF8String:argv[optind]]]; + } + } else { + LogError(@"Unable to parse command line arguments."); + PrintUsage(); + exit(kExitInvalidArguments); + } + + // Make sure the app path provided is legit. + DTiPhoneSimulatorApplicationSpecifier* appSpec = BuildAppSpec(appPath); + if (!appSpec) { + LogError(@"Invalid app path: %@", appPath); + exit(kExitInitializationFailure); + } + + // Make sure the SDK path provided is legit (or nil). + DTiPhoneSimulatorSystemRoot* systemRoot = BuildSystemRoot(sdkVersion); + if (!systemRoot) { + LogError(@"Invalid SDK version: %@", sdkVersion); + PrintSupportedDevices(); + exit(kExitInitializationFailure); + } + + // Get the paths for stdout and stderr so the simulated app's output will show + // up in the caller's stdout/stderr. + NSString* outputDir = CreateTempDirectory(@"iossim-XXXXXX"); + NSString* stdioPath = [outputDir stringByAppendingPathComponent:@"stdio.txt"]; + + // Determine the deviceFamily based on the deviceName + NSNumber* deviceFamily = nil; + if (IsRunningWithXcode6OrLater()) { +#if defined(IOSSIM_USE_XCODE_6) + Class simDeviceTypeClass = FindClassByName(@"SimDeviceType"); + if ([simDeviceTypeClass supportedDeviceTypesByName][deviceName] == nil) { + LogError(@"Invalid device name: %@.", deviceName); + PrintSupportedDevices(); + exit(kExitInvalidArguments); + } +#endif // IOSSIM_USE_XCODE_6 + } else { + if (!deviceName || CaseInsensitivePrefixSearch(deviceName, @"iPhone")) { + deviceFamily = [NSNumber numberWithInt:kIPhoneFamily]; + } else if (CaseInsensitivePrefixSearch(deviceName, @"iPad")) { + deviceFamily = [NSNumber numberWithInt:kIPadFamily]; + } + else { + LogError(@"Invalid device name: %@. Must begin with 'iPhone' or 'iPad'", + deviceName); + exit(kExitInvalidArguments); + } + } + + // Set up the user home directory for the simulator only if a non-default + // value was specified. + if (simHomePath) { + if (!InitializeSimulatorUserHome(simHomePath)) { + LogError(@"Unable to initialize home directory for simulator: %@", + simHomePath); + exit(kExitInitializationFailure); + } + } else { + simHomePath = NSHomeDirectory(); + } + + // Create the config and simulator session. + DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, + systemRoot, + stdioPath, + stdioPath, + appArgs, + appEnv, + deviceFamily, + deviceName); + SimulatorDelegate* delegate = + [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath + developerDir:developerDir + simulatorHome:simHomePath] autorelease]; + DTiPhoneSimulatorSession* session = BuildSession(delegate); + + // Start the simulator session. + NSError* error; + BOOL started = [session requestStartWithConfig:config + timeout:sessionStartTimeout + error:&error]; + + // Spin the runtime indefinitely. When the delegate gets the message that the + // app has quit it will exit this program. + if (started) { + [[NSRunLoop mainRunLoop] run]; + } else { + LogError(@"Simulator failed request to start: \"%@\" (%@:%ld)", + [error localizedDescription], + [error domain], static_cast([error code])); + } + + // Note that this code is only executed if the simulator fails to start + // because once the main run loop is started, only the delegate calling + // exit() will end the program. + [pool drain]; + return kExitFailure; +} diff --git a/engine/src/flutter/testing/iossim/redirect-stdout.sh b/engine/src/flutter/testing/iossim/redirect-stdout.sh new file mode 100755 index 0000000000..446550368b --- /dev/null +++ b/engine/src/flutter/testing/iossim/redirect-stdout.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# Copyright (c) 2012 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. +# +# This script executes the command given as the first argument, strips +# cxx_destruct from stdout, and redirects the remaining stdout to the file given +# as the second argument. +# +# Example: Write the text 'foo' to a file called out.txt: +# RedirectStdout.sh "echo foo" out.txt +# +# This script is invoked from iossim.gyp in order to redirect the output of +# class-dump to a file (because gyp actions don't support redirecting output). +# This script also removes all lines with cxx_destruct. Perhaps newer versions +# of class-dump will fix this issue. As of 3.5, 'cxx_destruct' still exists. + +if [ ${#} -ne 2 ] ; then + echo "usage: ${0} " + exit 2 +fi + +echo "// Treat class-dump output as a system header." > $2 +echo "#pragma clang system_header" >> $2 +$1 | sed /cxx_destruct/d >> $2 diff --git a/engine/src/flutter/testing/legion/__init__.py b/engine/src/flutter/testing/legion/__init__.py new file mode 100644 index 0000000000..50b23dff63 --- /dev/null +++ b/engine/src/flutter/testing/legion/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2015 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. diff --git a/engine/src/flutter/testing/legion/common_lib.py b/engine/src/flutter/testing/legion/common_lib.py new file mode 100644 index 0000000000..6e7954bbee --- /dev/null +++ b/engine/src/flutter/testing/legion/common_lib.py @@ -0,0 +1,43 @@ +# Copyright 2015 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. + +"""Common library methods used by both coordinator and task machines.""" + +import argparse +import logging +import os +import socket +import xmlrpclib + +LOGGING_LEVELS = ['DEBUG', 'INFO', 'WARNING', 'WARN', 'ERROR'] +MY_IP = socket.gethostbyname(socket.gethostname()) +SERVER_ADDRESS = '' +SERVER_PORT = 31710 +DEFAULT_TIMEOUT_SECS = 20 * 60 # 30 minutes +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) +SWARMING_DIR = os.path.join(THIS_DIR, '..', '..', 'tools', 'swarming_client') + + +def InitLogging(): + """Initialize the logging module. + + Raises: + argparse.ArgumentError if the --verbosity arg is incorrect. + """ + parser = argparse.ArgumentParser() + logging_action = parser.add_argument('--verbosity', default='INFO') + args, _ = parser.parse_known_args() + if args.verbosity not in LOGGING_LEVELS: + raise argparse.ArgumentError( + logging_action, 'Only levels %s supported' % str(LOGGING_LEVELS)) + logging.basicConfig( + format='%(asctime)s %(filename)s:%(lineno)s %(levelname)s] %(message)s', + datefmt='%H:%M:%S', level=args.verbosity) + + +def ConnectToServer(server): + """Connect to an RPC server.""" + addr = 'http://%s:%d' % (server, SERVER_PORT) + logging.debug('Connecting to RPC server at %s', addr) + return xmlrpclib.Server(addr, allow_none=True) diff --git a/engine/src/flutter/testing/legion/examples/hello_world/controller_test.isolate b/engine/src/flutter/testing/legion/examples/hello_world/controller_test.isolate new file mode 100644 index 0000000000..0cc5a60166 --- /dev/null +++ b/engine/src/flutter/testing/legion/examples/hello_world/controller_test.isolate @@ -0,0 +1,21 @@ +# Copyright 2015 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. + +{ + 'includes': [ + '../../legion.isolate', + ], + 'conditions': [ + ['multi_machine == 1', { + 'variables': { + 'command': [ + 'controller_test.py', + ], + 'files': [ + 'controller_test.py', + ], + }, + }], + ] +} diff --git a/engine/src/flutter/testing/legion/examples/hello_world/controller_test.py b/engine/src/flutter/testing/legion/examples/hello_world/controller_test.py new file mode 100755 index 0000000000..8253e9eb36 --- /dev/null +++ b/engine/src/flutter/testing/legion/examples/hello_world/controller_test.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# Copyright 2015 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 simple host test module. + +This module runs on the host machine and is responsible for creating 2 +task machines, waiting for them, and running RPC calls on them. +""" + +import argparse +import logging +import os +import sys +import time + +# Map the testing directory so we can import legion.legion_test. +TESTING_DIR = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + '..', '..', '..', '..', 'testing') +sys.path.append(TESTING_DIR) + +from legion import legion_test_case + + +class ExampleTestController(legion_test_case.TestCase): + """A simple example controller for a test.""" + + @classmethod + def CreateTestTask(cls): + """Create a new task.""" + parser = argparse.ArgumentParser() + parser.add_argument('--task-hash') + parser.add_argument('--os', default='Ubuntu-14.04') + args, _ = parser.parse_known_args() + + task = cls.CreateTask( + isolated_hash=args.task_hash, + dimensions={'os': args.os}, + idle_timeout_secs=90, + connection_timeout_secs=90, + verbosity=logging.DEBUG) + task.Create() + return task + + @classmethod + def setUpClass(cls): + """Creates the task machines and waits until they connect.""" + cls.task1 = cls.CreateTestTask() + cls.task2 = cls.CreateTestTask() + cls.task1.WaitForConnection() + cls.task2.WaitForConnection() + + def testCallEcho(self): + """Tests rpc.Echo on a task.""" + logging.info('Calling Echo on %s', self.task2.name) + self.assertEqual(self.task2.rpc.Echo('foo'), 'echo foo') + + def testLaunchTaskBinary(self): + """Call task_test.py 'name' on the tasks.""" + self.VerifyTaskBinaryLaunched(self.task1) + self.VerifyTaskBinaryLaunched(self.task2) + + def VerifyTaskBinaryLaunched(self, task): + logging.info( + 'Calling Process to run "./task_test.py %s"', task.name) + proc = task.Process(['./task_test.py', task.name]) + proc.Wait() + self.assertEqual(proc.GetReturncode(), 0) + self.assertIn(task.name, proc.ReadStdout()) + self.assertEquals(proc.ReadStderr(), '') + proc.Delete() + + +if __name__ == '__main__': + legion_test_case.main() diff --git a/engine/src/flutter/testing/legion/examples/hello_world/task_test.isolate b/engine/src/flutter/testing/legion/examples/hello_world/task_test.isolate new file mode 100644 index 0000000000..1322f310f2 --- /dev/null +++ b/engine/src/flutter/testing/legion/examples/hello_world/task_test.isolate @@ -0,0 +1,23 @@ +# Copyright 2015 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. + +{ + 'includes': [ + '../../legion.isolate' + ], + 'conditions': [ + ['multi_machine == 1', { + 'variables': { + 'command': [ + 'python', + '../../run_task.py', + ], + 'files': [ + 'task_test.isolate', + 'task_test.py', + ], + }, + }], + ], +} diff --git a/engine/src/flutter/testing/legion/examples/hello_world/task_test.py b/engine/src/flutter/testing/legion/examples/hello_world/task_test.py new file mode 100755 index 0000000000..0333f7bb0a --- /dev/null +++ b/engine/src/flutter/testing/legion/examples/hello_world/task_test.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# Copyright 2015 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 simple client test module. + +This module is invoked by the host by calling the client controller's +Subprocess RPC method. The name is passed in as a required argument on the +command line. +""" + +import argparse +import os +import sys + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('name') + args = parser.parse_args() + print 'Hello world from', args.name + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/testing/legion/examples/subprocess/subprocess_test.isolate b/engine/src/flutter/testing/legion/examples/subprocess/subprocess_test.isolate new file mode 100644 index 0000000000..6bcf6924cc --- /dev/null +++ b/engine/src/flutter/testing/legion/examples/subprocess/subprocess_test.isolate @@ -0,0 +1,21 @@ +# Copyright 2015 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. + +{ + 'includes': [ + '../../legion.isolate', + ], + 'conditions': [ + ['multi_machine == 1', { + 'variables': { + 'command': [ + 'subprocess_test.py', + ], + 'files': [ + 'subprocess_test.py', + ], + }, + }], + ] +} diff --git a/engine/src/flutter/testing/legion/examples/subprocess/subprocess_test.py b/engine/src/flutter/testing/legion/examples/subprocess/subprocess_test.py new file mode 100755 index 0000000000..cea871a405 --- /dev/null +++ b/engine/src/flutter/testing/legion/examples/subprocess/subprocess_test.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# Copyright 2015 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 host test module demonstrating interacting with remote subprocesses.""" + +import argparse +import logging +import os +import sys +import time +import xmlrpclib + +# Map the testing directory so we can import legion.legion_test. +TESTING_DIR = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + '..', '..', '..', '..', 'testing') +sys.path.append(TESTING_DIR) + +from legion import legion_test_case + + +class ExampleTestController(legion_test_case.TestCase): + """An example controller using the remote subprocess functions.""" + + @classmethod + def setUpClass(cls): + """Creates the task machine and waits until it connects.""" + parser = argparse.ArgumentParser() + parser.add_argument('--task-hash') + parser.add_argument('--os', default='Ubuntu-14.04') + args, _ = parser.parse_known_args() + + cls.task = cls.CreateTask( + isolated_hash=args.task_hash, + dimensions={'os': args.os}, + idle_timeout_secs=90, + connection_timeout_secs=90, + verbosity=logging.DEBUG) + cls.task.Create() + cls.task.WaitForConnection() + + def testMultipleProcesses(self): + """Tests that processes can be run and controlled simultaneously.""" + start = time.time() + logging.info('Starting "sleep 10" and "sleep 20"') + sleep10 = self.task.Process(['sleep', '10']) + sleep20 = self.task.Process(['sleep', '20']) + + logging.info('Waiting for sleep 10 to finish and verifying timing') + sleep10.Wait() + elapsed = time.time() - start + self.assertGreaterEqual(elapsed, 10) + self.assertLess(elapsed, 11) + + logging.info('Waiting for sleep 20 to finish and verifying timing') + sleep20.Wait() + elapsed = time.time() - start + self.assertGreaterEqual(elapsed, 20) + + sleep10.Delete() + sleep20.Delete() + + def testTerminate(self): + """Tests that a process can be correctly terminated.""" + start = time.time() + + logging.info('Starting "sleep 20"') + sleep20 = self.task.Process(['sleep', '20']) + logging.info('Calling Terminate()') + sleep20.Terminate() + try: + logging.info('Trying to wait for sleep 20 to complete') + sleep20.Wait() + except xmlrpclib.Fault: + pass + finally: + sleep20.Delete() + logging.info('Checking to make sure sleep 20 was actually terminated') + self.assertLess(time.time() - start, 20) + + def testLs(self): + """Tests that the returned results from a process are correct.""" + logging.info('Calling "ls"') + ls = self.task.Process(['ls']) + logging.info('Trying to wait for ls to complete') + ls.Wait() + logging.info('Checking that ls completed and returned the correct results') + self.assertEqual(ls.GetReturncode(), 0) + self.assertIn('task.isolate', ls.ReadStdout()) + + +if __name__ == '__main__': + legion_test_case.main() diff --git a/engine/src/flutter/testing/legion/examples/subprocess/task.isolate b/engine/src/flutter/testing/legion/examples/subprocess/task.isolate new file mode 100644 index 0000000000..534275dfed --- /dev/null +++ b/engine/src/flutter/testing/legion/examples/subprocess/task.isolate @@ -0,0 +1,22 @@ +# Copyright 2015 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. + +{ + 'includes': [ + '../../legion.isolate' + ], + 'conditions': [ + ['multi_machine == 1', { + 'variables': { + 'command': [ + 'python', + '../../run_task.py', + ], + 'files': [ + 'task.isolate' + ], + }, + }], + ], +} diff --git a/engine/src/flutter/testing/legion/legion.isolate b/engine/src/flutter/testing/legion/legion.isolate new file mode 100644 index 0000000000..18e0b3b092 --- /dev/null +++ b/engine/src/flutter/testing/legion/legion.isolate @@ -0,0 +1,22 @@ +# Copyright 2015 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. + +{ + 'variables': { + 'files': [ + '__init__.py', + 'common_lib.py', + 'legion_test_case.py', + 'legion.isolate', + 'process.py', + 'rpc_methods.py', + 'rpc_server.py', + 'run_task.py', + 'task_controller.py', + 'task_registration_server.py', + 'test_controller.py', + '../../tools/swarming_client/', + ], + }, +} diff --git a/engine/src/flutter/testing/legion/legion_test_case.py b/engine/src/flutter/testing/legion/legion_test_case.py new file mode 100644 index 0000000000..9514e8e19b --- /dev/null +++ b/engine/src/flutter/testing/legion/legion_test_case.py @@ -0,0 +1,135 @@ +# Copyright 2015 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. + +"""Adds unittest-esque functionality to Legion.""" + +import argparse +import logging +import sys +import unittest + +#pylint: disable=relative-import +import common_lib +import task_controller +import task_registration_server + +BANNER_WIDTH = 80 + + +class TestCase(unittest.TestCase): + """Test case class with added Legion support.""" + + _registration_server = None + _initialized = False + + @classmethod + def __new__(cls, *args, **kwargs): + """Initialize the class and return a new instance.""" + cls._InitializeClass() + return super(TestCase, cls).__new__(*args, **kwargs) + + def __init__(self, test_name='runTest'): + super(TestCase, self).__init__(test_name) + method = getattr(self, test_name, None) + if method: + # Install the _RunTest method + self._TestMethod = method + setattr(self, test_name, self._RunTest) + + def _RunTest(self): + """Runs the test method and provides banner info and error reporting.""" + self._LogInfoBanner(self._testMethodName, self.shortDescription()) + try: + return self._TestMethod() + except: + exc_info = sys.exc_info() + logging.error('', exc_info=exc_info) + raise exc_info[0], exc_info[1], exc_info[2] + + @classmethod + def _InitializeClass(cls): + """Handles class level initialization. + + There are 2 types of setup/teardown methods that always need to be run: + 1) Framework level setup/teardown + 2) Test case level setup/teardown + + This method installs handlers in place of setUpClass and tearDownClass that + will ensure both types of setup/teardown methods are called correctly. + """ + if cls._initialized: + return + cls._OriginalSetUpClassMethod = cls.setUpClass + cls.setUpClass = cls._HandleSetUpClass + cls._OriginalTearDownClassMethod = cls.tearDownClass + cls.tearDownClass = cls._HandleTearDownClass + cls._initialized = True + + @classmethod + def _LogInfoBanner(cls, method_name, method_doc=None): + """Formats and logs test case information.""" + logging.info('*' * BANNER_WIDTH) + logging.info(method_name.center(BANNER_WIDTH)) + if method_doc: + for line in method_doc.split('\n'): + logging.info(line.center(BANNER_WIDTH)) + logging.info('*' * BANNER_WIDTH) + + @classmethod + def CreateTask(cls, *args, **kwargs): + """Convenience method to create a new task.""" + task = task_controller.TaskController(*args, **kwargs) + cls._registration_server.RegisterTaskCallback( + task.otp, task.OnConnect) + return task + + @classmethod + def _SetUpFramework(cls): + """Perform the framework-specific setup operations.""" + cls._registration_server = ( + task_registration_server.TaskRegistrationServer()) + cls._registration_server.Start() + + @classmethod + def _TearDownFramework(cls): + """Perform the framework-specific teardown operations.""" + if cls._registration_server: + cls._registration_server.Shutdown() + task_controller.TaskController.ReleaseAllTasks() + + @classmethod + def _HandleSetUpClass(cls): + """Performs common class-level setup operations. + + This method performs test-wide setup such as starting the registration + server and then calls the original setUpClass method.""" + try: + common_lib.InitLogging() + cls._LogInfoBanner('setUpClass', 'Performs class level setup.') + cls._SetUpFramework() + cls._OriginalSetUpClassMethod() + except: + # Make sure we tear down in case of any exceptions + cls._HandleTearDownClass(setup_failed=True) + exc_info = sys.exc_info() + logging.error('', exc_info=exc_info) + raise exc_info[0], exc_info[1], exc_info[2] + + @classmethod + def _HandleTearDownClass(cls, setup_failed=False): + """Performs common class-level tear down operations. + + This method calls the original tearDownClass then performs test-wide + tear down such as stopping the registration server. + """ + cls._LogInfoBanner('tearDownClass', 'Performs class level tear down.') + try: + if not setup_failed: + cls._OriginalTearDownClassMethod() + finally: + cls._TearDownFramework() + + +def main(): + unittest.main(verbosity=0, argv=sys.argv[:1]) diff --git a/engine/src/flutter/testing/legion/process.py b/engine/src/flutter/testing/legion/process.py new file mode 100644 index 0000000000..356db6131b --- /dev/null +++ b/engine/src/flutter/testing/legion/process.py @@ -0,0 +1,247 @@ +# Copyright 2015 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. + +"""RPC compatible subprocess-type module. + +This module defined both a task-side process class as well as a controller-side +process wrapper for easier access and usage of the task-side process. +""" + +import logging +import subprocess +import sys +import threading + +#pylint: disable=relative-import +import common_lib + +# Map swarming_client to use subprocess42 +sys.path.append(common_lib.SWARMING_DIR) + +from utils import subprocess42 + + +class ControllerProcessWrapper(object): + """Controller-side process wrapper class. + + This class provides a more intuitive interface to task-side processes + than calling the methods directly using the RPC object. + """ + + def __init__(self, rpc, cmd, verbose=False, detached=False, cwd=None): + self._rpc = rpc + self._id = rpc.subprocess.Process(cmd) + if verbose: + self._rpc.subprocess.SetVerbose(self._id) + if detached: + self._rpc.subprocess.SetDetached(self._id) + if cwd: + self._rpc.subprocess.SetCwd(self._rpc, cwd) + self._rpc.subprocess.Start(self._id) + + def Terminate(self): + logging.debug('Terminating process %s', self._id) + return self._rpc.subprocess.Terminate(self._id) + + def Kill(self): + logging.debug('Killing process %s', self._id) + self._rpc.subprocess.Kill(self._id) + + def Delete(self): + return self._rpc.subprocess.Delete(self._id) + + def GetReturncode(self): + return self._rpc.subprocess.GetReturncode(self._id) + + def ReadStdout(self): + """Returns all stdout since the last call to ReadStdout. + + This call allows the user to read stdout while the process is running. + However each call will flush the local stdout buffer. In order to make + multiple calls to ReadStdout and to retain the entire output the results + of this call will need to be buffered in the calling code. + """ + return self._rpc.subprocess.ReadStdout(self._id) + + def ReadStderr(self): + """Returns all stderr read since the last call to ReadStderr. + + See ReadStdout for additional details. + """ + return self._rpc.subprocess.ReadStderr(self._id) + + def ReadOutput(self): + """Returns the (stdout, stderr) since the last Read* call. + + See ReadStdout for additional details. + """ + return self._rpc.subprocess.ReadOutput(self._id) + + def Wait(self): + return self._rpc.subprocess.Wait(self._id) + + def Poll(self): + return self._rpc.subprocess.Poll(self._id) + + def GetPid(self): + return self._rpc.subprocess.GetPid(self._id) + + + +class Process(object): + """Implements a task-side non-blocking subprocess. + + This non-blocking subprocess allows the caller to continue operating while + also able to interact with this subprocess based on a key returned to + the caller at the time of creation. + + Creation args are set via Set* methods called after calling Process but + before calling Start. This is due to a limitation of the XML-RPC + implementation not supporting keyword arguments. + """ + + _processes = {} + _process_next_id = 0 + _creation_lock = threading.Lock() + + def __init__(self, cmd): + self.stdout = '' + self.stderr = '' + self.cmd = cmd + self.proc = None + self.cwd = None + self.verbose = False + self.detached = False + self.data_lock = threading.Lock() + + def __str__(self): + return '%r, cwd=%r, verbose=%r, detached=%r' % ( + self.cmd, self.cwd, self.verbose, self.detached) + + def _reader(self): + for pipe, data in self.proc.yield_any(): + with self.data_lock: + if pipe == 'stdout': + self.stdout += data + if self.verbose: + sys.stdout.write(data) + else: + self.stderr += data + if self.verbose: + sys.stderr.write(data) + + @classmethod + def KillAll(cls): + for key in cls._processes: + cls.Kill(key) + + @classmethod + def Process(cls, cmd): + with cls._creation_lock: + key = 'Process%d' % cls._process_next_id + cls._process_next_id += 1 + logging.debug('Creating process %s with cmd %r', key, cmd) + process = cls(cmd) + cls._processes[key] = process + return key + + def _Start(self): + logging.info('Starting process %s', self) + self.proc = subprocess42.Popen(self.cmd, stdout=subprocess42.PIPE, + stderr=subprocess42.PIPE, + detached=self.detached, cwd=self.cwd) + threading.Thread(target=self._reader).start() + + @classmethod + def Start(cls, key): + cls._processes[key]._Start() + + @classmethod + def SetCwd(cls, key, cwd): + """Sets the process's cwd.""" + logging.debug('Setting %s cwd to %s', key, cwd) + cls._processes[key].cwd = cwd + + @classmethod + def SetDetached(cls, key): + """Creates a detached process.""" + logging.debug('Setting %s.detached = True', key) + cls._processes[key].detached = True + + @classmethod + def SetVerbose(cls, key): + """Sets the stdout and stderr to be emitted locally.""" + logging.debug('Setting %s.verbose = True', key) + cls._processes[key].verbose = True + + @classmethod + def Terminate(cls, key): + logging.debug('Terminating process %s', key) + cls._processes[key].proc.terminate() + + @classmethod + def Kill(cls, key): + logging.debug('Killing process %s', key) + cls._processes[key].proc.kill() + + @classmethod + def Delete(cls, key): + if cls.GetReturncode(key) is None: + logging.warning('Killing %s before deleting it', key) + cls.Kill(key) + logging.debug('Deleting process %s', key) + cls._processes.pop(key) + + @classmethod + def GetReturncode(cls, key): + return cls._processes[key].proc.returncode + + @classmethod + def ReadStdout(cls, key): + """Returns all stdout since the last call to ReadStdout. + + This call allows the user to read stdout while the process is running. + However each call will flush the local stdout buffer. In order to make + multiple calls to ReadStdout and to retain the entire output the results + of this call will need to be buffered in the calling code. + """ + proc = cls._processes[key] + with proc.data_lock: + # Perform a "read" on the stdout data + stdout = proc.stdout + proc.stdout = '' + return stdout + + @classmethod + def ReadStderr(cls, key): + """Returns all stderr read since the last call to ReadStderr. + + See ReadStdout for additional details. + """ + proc = cls._processes[key] + with proc.data_lock: + # Perform a "read" on the stderr data + stderr = proc.stderr + proc.stderr = '' + return stderr + + @classmethod + def ReadOutput(cls, key): + """Returns the (stdout, stderr) since the last Read* call. + + See ReadStdout for additional details. + """ + return cls.ReadStdout(key), cls.ReadStderr(key) + + @classmethod + def Wait(cls, key): + return cls._processes[key].proc.wait() + + @classmethod + def Poll(cls, key): + return cls._processes[key].proc.poll() + + @classmethod + def GetPid(cls, key): + return cls._processes[key].proc.pid diff --git a/engine/src/flutter/testing/legion/rpc_methods.py b/engine/src/flutter/testing/legion/rpc_methods.py new file mode 100644 index 0000000000..24d0312c25 --- /dev/null +++ b/engine/src/flutter/testing/legion/rpc_methods.py @@ -0,0 +1,51 @@ +# Copyright 2015 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. + +"""Defines the task RPC methods.""" + +import logging +import os +import sys +import threading + +#pylint: disable=relative-import +import process + + +class RPCMethods(object): + """Class exposing RPC methods.""" + + _dotted_whitelist = ['subprocess'] + + def __init__(self, server): + self._server = server + self.subprocess = process.Process + + def _dispatch(self, method, params): + obj = self + if '.' in method: + # Allow only white listed dotted names + name, method = method.split('.') + assert name in self._dotted_whitelist + obj = getattr(self, name) + return getattr(obj, method)(*params) + + def Echo(self, message): + """Simple RPC method to print and return a message.""" + logging.info('Echoing %s', message) + return 'echo %s' % str(message) + + def AbsPath(self, path): + """Returns the absolute path.""" + return os.path.abspath(path) + + def Quit(self): + """Call _server.shutdown in another thread. + + This is needed because server.shutdown waits for the server to actually + quit. However the server cannot shutdown until it completes handling this + call. Calling this in the same thread results in a deadlock. + """ + t = threading.Thread(target=self._server.shutdown) + t.start() diff --git a/engine/src/flutter/testing/legion/rpc_server.py b/engine/src/flutter/testing/legion/rpc_server.py new file mode 100644 index 0000000000..43b431707e --- /dev/null +++ b/engine/src/flutter/testing/legion/rpc_server.py @@ -0,0 +1,128 @@ +# Copyright 2015 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. + +"""The task RPC server code. + +This server is an XML-RPC server which serves code from +rpc_methods.RPCMethods. + +This server will run until shutdown is called on the server object. This can +be achieved in 2 ways: + +- Calling the Quit RPC method defined in RPCMethods +- Not receiving any calls within the idle_timeout_secs time. +""" + +import logging +import threading +import time +import xmlrpclib +import SimpleXMLRPCServer +import SocketServer + +#pylint: disable=relative-import +import common_lib +import rpc_methods + + +class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): + """Restricts access to only specified IP address. + + This call assumes the server is RPCServer. + """ + + def do_POST(self): + """Verifies the task is authorized to perform RPCs.""" + if self.client_address[0] != self.server.authorized_address: + logging.error('Received unauthorized RPC request from %s', + self.task_address[0]) + self.send_response(403) + response = 'Forbidden' + self.send_header('Content-type', 'text/plain') + self.send_header('Content-length', str(len(response))) + self.end_headers() + self.wfile.write(response) + else: + return SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self) + + +class RPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, + SocketServer.ThreadingMixIn): + """Restricts all endpoints to only specified IP addresses.""" + + def __init__(self, authorized_address, + idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS): + SimpleXMLRPCServer.SimpleXMLRPCServer.__init__( + self, (common_lib.SERVER_ADDRESS, common_lib.SERVER_PORT), + allow_none=True, logRequests=False, + requestHandler=RequestHandler) + + self.authorized_address = authorized_address + self.idle_timeout_secs = idle_timeout_secs + self.register_instance(rpc_methods.RPCMethods(self)) + + self._shutdown_requested_event = threading.Event() + self._rpc_received_event = threading.Event() + self._idle_thread = threading.Thread(target=self._CheckForIdleQuit) + + def shutdown(self): + """Shutdown the server. + + This overloaded method sets the _shutdown_requested_event to allow the + idle timeout thread to quit. + """ + self._shutdown_requested_event.set() + SimpleXMLRPCServer.SimpleXMLRPCServer.shutdown(self) + logging.info('Server shutdown complete') + + def serve_forever(self, poll_interval=0.5): + """Serve forever. + + This overloaded method starts the idle timeout thread before calling + serve_forever. This ensures the idle timer thread doesn't get started + without the server running. + + Args: + poll_interval: The interval to poll for shutdown. + """ + logging.info('RPC server starting') + self._idle_thread.start() + SimpleXMLRPCServer.SimpleXMLRPCServer.serve_forever(self, poll_interval) + + def _dispatch(self, method, params): + """Dispatch the call to the correct method with the provided params. + + This overloaded method adds logging to help trace connection and + call problems. + + Args: + method: The method name to call. + params: A tuple of parameters to pass. + + Returns: + The result of the parent class' _dispatch method. + """ + logging.debug('Calling %s%s', method, params) + self._rpc_received_event.set() + return SimpleXMLRPCServer.SimpleXMLRPCServer._dispatch(self, method, params) + + def _CheckForIdleQuit(self): + """Check for, and exit, if the server is idle for too long. + + This method must be run in a separate thread to avoid a deadlock when + calling server.shutdown. + """ + timeout = time.time() + self.idle_timeout_secs + while time.time() < timeout: + if self._shutdown_requested_event.is_set(): + # An external source called shutdown() + return + elif self._rpc_received_event.is_set(): + logging.debug('Resetting the idle timeout') + timeout = time.time() + self.idle_timeout_secs + self._rpc_received_event.clear() + time.sleep(1) + # We timed out, kill the server + logging.warning('Shutting down the server due to the idle timeout') + self.shutdown() diff --git a/engine/src/flutter/testing/legion/run_task.py b/engine/src/flutter/testing/legion/run_task.py new file mode 100755 index 0000000000..26c6e5c877 --- /dev/null +++ b/engine/src/flutter/testing/legion/run_task.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +"""The main task entrypoint.""" + +import argparse +import logging +import socket +import sys +import time + +#pylint: disable=relative-import +import common_lib +import rpc_server + + +def main(): + print ' '.join(sys.argv) + common_lib.InitLogging() + logging.info('Task starting') + + parser = argparse.ArgumentParser() + parser.add_argument('--otp', + help='One time token used to authenticate with the host') + parser.add_argument('--controller', + help='The ip address of the controller machine') + parser.add_argument('--idle-timeout', type=int, + default=common_lib.DEFAULT_TIMEOUT_SECS, + help='The idle timeout for the rpc server in seconds') + args, _ = parser.parse_known_args() + + logging.info( + 'Registering with registration server at %s using OTP "%s"', + args.controller, args.otp) + common_lib.ConnectToServer(args.controller).RegisterTask( + args.otp, common_lib.MY_IP) + + server = rpc_server.RPCServer(args.controller, args.idle_timeout) + + server.serve_forever() + logging.info('Server shutdown complete') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/testing/legion/task_controller.py b/engine/src/flutter/testing/legion/task_controller.py new file mode 100644 index 0000000000..9514f44fdd --- /dev/null +++ b/engine/src/flutter/testing/legion/task_controller.py @@ -0,0 +1,213 @@ +# Copyright 2015 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. + +"""Defines the task controller library.""" + +import argparse +import datetime +import logging +import os +import socket +import subprocess +import sys +import tempfile +import threading +import xmlrpclib + +#pylint: disable=relative-import +import common_lib +import process + +ISOLATE_PY = os.path.join(common_lib.SWARMING_DIR, 'isolate.py') +SWARMING_PY = os.path.join(common_lib.SWARMING_DIR, 'swarming.py') + + +class Error(Exception): + pass + + +class ConnectionTimeoutError(Error): + pass + + +class TaskController(object): + """Provisions, configures, and controls a task machine. + + This class is an abstraction of a physical task machine. It provides an + end to end API for controlling a task machine. Operations on the task machine + are performed using the instance's "rpc" property. A simple end to end + scenario is as follows: + + task = TaskController(...) + task.Create() + task.WaitForConnection() + proc = task.rpc.subprocess.Popen(['ls']) + print task.rpc.subprocess.GetStdout(proc) + task.Release() + """ + + _task_count = 0 + _tasks = [] + + def __init__(self, isolated_hash, dimensions, priority=100, + idle_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS, + connection_timeout_secs=common_lib.DEFAULT_TIMEOUT_SECS, + verbosity='ERROR', name=None, run_id=None): + assert isinstance(dimensions, dict) + type(self)._tasks.append(self) + type(self)._task_count += 1 + self.verbosity = verbosity + self._name = name or 'Task%d' % type(self)._task_count + self._priority = priority + self._isolated_hash = isolated_hash + self._idle_timeout_secs = idle_timeout_secs + self._dimensions = dimensions + self._connect_event = threading.Event() + self._connected = False + self._ip_address = None + self._otp = self._CreateOTP() + self._rpc = None + + run_id = run_id or datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S') + self._task_name = '%s/%s/%s' % ( + os.path.splitext(sys.argv[0])[0], self._name, run_id) + + parser = argparse.ArgumentParser() + parser.add_argument('--isolate-server') + parser.add_argument('--swarming-server') + parser.add_argument('--task-connection-timeout-secs', + default=common_lib.DEFAULT_TIMEOUT_SECS) + args, _ = parser.parse_known_args() + + self._isolate_server = args.isolate_server + self._swarming_server = args.swarming_server + self._connection_timeout_secs = (connection_timeout_secs or + args.task_connection_timeout_secs) + + @property + def name(self): + return self._name + + @property + def otp(self): + return self._otp + + @property + def connected(self): + return self._connected + + @property + def connect_event(self): + return self._connect_event + + @property + def rpc(self): + return self._rpc + + @property + def verbosity(self): + return self._verbosity + + @verbosity.setter + def verbosity(self, level): + """Sets the verbosity level as a string. + + Either a string ('INFO', 'DEBUG', etc) or a logging level (logging.INFO, + logging.DEBUG, etc) is allowed. + """ + assert isinstance(level, (str, int)) + if isinstance(level, int): + level = logging.getLevelName(level) + self._verbosity = level #pylint: disable=attribute-defined-outside-init + + @classmethod + def ReleaseAllTasks(cls): + for task in cls._tasks: + task.Release() + + def Process(self, cmd, verbose=False, detached=False, cwd=None): + return process.ControllerProcessWrapper( + self.rpc, cmd, verbose, detached, cwd) + + def _CreateOTP(self): + """Creates the OTP.""" + controller_name = socket.gethostname() + test_name = os.path.basename(sys.argv[0]) + creation_time = datetime.datetime.utcnow() + otp = 'task:%s controller:%s test:%s creation:%s' % ( + self._name, controller_name, test_name, creation_time) + return otp + + def Create(self): + """Creates the task machine.""" + logging.info('Creating %s', self.name) + self._connect_event.clear() + self._ExecuteSwarming() + + def WaitForConnection(self): + """Waits for the task machine to connect. + + Raises: + ConnectionTimeoutError if the task doesn't connect in time. + """ + logging.info('Waiting for %s to connect with a timeout of %d seconds', + self._name, self._connection_timeout_secs) + self._connect_event.wait(self._connection_timeout_secs) + if not self._connect_event.is_set(): + raise ConnectionTimeoutError('%s failed to connect' % self.name) + + def Release(self): + """Quits the task's RPC server so it can release the machine.""" + if self._rpc is not None and self._connected: + logging.info('Releasing %s', self._name) + try: + self._rpc.Quit() + except (socket.error, xmlrpclib.Fault): + logging.error('Unable to connect to %s to call Quit', self.name) + self._rpc = None + self._connected = False + + def _ExecuteSwarming(self): + """Executes swarming.py.""" + cmd = [ + 'python', + SWARMING_PY, + 'trigger', + self._isolated_hash, + '--priority', str(self._priority), + '--task-name', self._task_name, + ] + + if self._isolate_server: + cmd.extend(['--isolate-server', self._isolate_server]) + if self._swarming_server: + cmd.extend(['--swarming', self._swarming_server]) + for key, value in self._dimensions.iteritems(): + cmd.extend(['--dimension', key, value]) + + cmd.extend([ + '--', + '--controller', common_lib.MY_IP, + '--otp', self._otp, + '--verbosity', self._verbosity, + '--idle-timeout', str(self._idle_timeout_secs), + ]) + + self._ExecuteProcess(cmd) + + def _ExecuteProcess(self, cmd): + """Executes a process, waits for it to complete, and checks for success.""" + logging.debug('Running %s', ' '.join(cmd)) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + _, stderr = p.communicate() + if p.returncode != 0: + raise Error(stderr) + + def OnConnect(self, ip_address): + """Receives task ip address on connection.""" + self._ip_address = ip_address + self._connected = True + self._rpc = common_lib.ConnectToServer(self._ip_address) + logging.info('%s connected from %s', self._name, ip_address) + self._connect_event.set() diff --git a/engine/src/flutter/testing/legion/task_registration_server.py b/engine/src/flutter/testing/legion/task_registration_server.py new file mode 100644 index 0000000000..fe92dcd5fc --- /dev/null +++ b/engine/src/flutter/testing/legion/task_registration_server.py @@ -0,0 +1,55 @@ +# Copyright 2015 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. + +"""The registration server used to register tasks. + +The registration server is started by the test controller and allows the tasks +to register themselves when they start. Authentication of the tasks controllers +is based on an OTP passed to the run_task binary on startup. +""" + +import logging +import threading +import xmlrpclib +import SimpleXMLRPCServer + +#pylint: disable=relative-import +import common_lib + + +class TaskRegistrationServer(object): + """Discovery server run on the host.""" + + def __init__(self): + self._expected_tasks = {} + self._rpc_server = None + self._thread = None + + def _RegisterTaskRPC(self, otp, ip): + """The RPC used by a task to register with the registration server.""" + assert otp in self._expected_tasks + cb = self._expected_tasks.pop(otp) + cb(ip) + + def RegisterTaskCallback(self, otp, callback): + """Registers a callback associated with an OTP.""" + assert callable(callback) + self._expected_tasks[otp] = callback + + def Start(self): + """Starts the registration server.""" + logging.info('Starting task registration server') + self._rpc_server = SimpleXMLRPCServer.SimpleXMLRPCServer( + (common_lib.SERVER_ADDRESS, common_lib.SERVER_PORT), + allow_none=True, logRequests=False) + self._rpc_server.register_function( + self._RegisterTaskRPC, 'RegisterTask') + self._thread = threading.Thread(target=self._rpc_server.serve_forever) + self._thread.start() + + def Shutdown(self): + """Shuts the discovery server down.""" + if self._thread and self._thread.is_alive(): + logging.info('Shutting down task registration server') + self._rpc_server.shutdown() diff --git a/engine/src/flutter/testing/legion/test_controller.py b/engine/src/flutter/testing/legion/test_controller.py new file mode 100644 index 0000000000..2703fadf09 --- /dev/null +++ b/engine/src/flutter/testing/legion/test_controller.py @@ -0,0 +1,69 @@ +# Copyright 2015 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. + +"""Defines the test controller base library. + +This module is the basis on which test controllers are built and executed. +""" + +import logging +import sys + +#pylint: disable=relative-import +import common_lib +import task_controller +import task_registration_server + + +class TestController(object): + """The base test controller class.""" + + def __init__(self): + self._registration_server = ( + task_registration_server.TaskRegistrationServer()) + + def SetUp(self): + """Setup method used by the subclass.""" + pass + + def RunTest(self): + """Main test method used by the subclass.""" + raise NotImplementedError() + + def TearDown(self): + """Teardown method used by the subclass.""" + pass + + def CreateNewTask(self, *args, **kwargs): + task = task_controller.TaskController(*args, **kwargs) + self._registration_server.RegisterTaskCallback( + task.otp, task.OnConnect) + return task + + def RunController(self): + """Main entry point for the controller.""" + print ' '.join(sys.argv) + common_lib.InitLogging() + self._registration_server.Start() + + error = None + tb = None + try: + self.SetUp() + self.RunTest() + except Exception as e: + # Defer raising exceptions until after TearDown is called. + error = e + tb = sys.exc_info()[-1] + try: + self.TearDown() + except Exception as e: + if not tb: + error = e + tb = sys.exc_info()[-1] + + self._registration_server.Shutdown() + task_controller.TaskController.ReleaseAllTasks() + if error: + raise error, None, tb #pylint: disable=raising-bad-type diff --git a/engine/src/flutter/testing/legion/tools/legion.py b/engine/src/flutter/testing/legion/tools/legion.py new file mode 100755 index 0000000000..7b671335ee --- /dev/null +++ b/engine/src/flutter/testing/legion/tools/legion.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# Copyright 2015 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 helper module to run Legion multi-machine tests. + +Example usage with 1 task machine: +$ testing/legion/tools/legion.py run \ + --controller-isolated out/Release/example_test_controller.isolated \ + --dimension os Ubuntu-14.04 \ + --task-name test-task-name \ + --task task_machine out/Release/example_task_machine.isolated + +Example usage with 2 task machines with the same isolated file: +$ testing/legion/tools/legion.py run \ + --controller-isolated out/Release/example_test_controller.isolated \ + --dimension os Ubuntu-14.04 \ + --task-name test-task-name \ + --task task_machine_1 out/Release/example_task_machine.isolated \ + --task task_machine_2 out/Release/example_task_machine.isolated + +Example usage with 2 task machines with different isolated file: +$ testing/legion/tools/legion.py run \ + --controller-isolated out/Release/example_test_controller.isolated \ + --dimension os Ubuntu-14.04 \ + --task-name test-task-name \ + --task task_machine_1 out/Release/example_task_machine_1.isolated \ + --task task_machine_2 out/Release/example_task_machine_2.isolated +""" + +import argparse +import logging +import os +import subprocess +import sys + + +THIS_DIR = os.path.split(__file__)[0] +SWARMING_DIR = os.path.join(THIS_DIR, '..', '..', '..', 'tools', + 'swarming_client') +ISOLATE_PY = os.path.join(SWARMING_DIR, 'isolate.py') +SWARMING_PY = os.path.join(SWARMING_DIR, 'swarming.py') +LOGGING_LEVELS = ['DEBUG', 'INFO', 'WARNING', 'ERROR'] + + +class Error(Exception): + pass + + +def GetArgs(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('action', choices=['run', 'trigger'], + help='The swarming action to perform.') + parser.add_argument('-f', '--format-only', action='store_true', + help='If true the .isolated files are archived but ' + 'swarming is not called, only the command line is built.') + parser.add_argument('--controller-isolated', required=True, + help='The isolated file for the test controller.') + parser.add_argument('--isolate-server', help='Optional. The isolated server ' + 'to use.') + parser.add_argument('--swarming-server', help='Optional. The swarming server ' + 'to use.') + parser.add_argument('--task-name', help='Optional. The swarming task name ' + 'to use.') + parser.add_argument('--dimension', action='append', dest='dimensions', + nargs=2, default=[], help='Dimensions to pass to ' + 'swarming.py. This is in the form of --dimension key ' + 'value. The minimum required is --dimension os ') + parser.add_argument('--task', action='append', dest='tasks', + nargs=2, default=[], help='List of task names used in ' + 'the test controller. This is in the form of --task name ' + '.isolated and is passed to the controller as --name ' + '.') + parser.add_argument('--controller-var', action='append', + dest='controller_vars', nargs=2, default=[], + help='Command line vars to pass to the controller. These ' + 'are in the form of --controller-var name value and are ' + 'passed to the controller as --name value.') + parser.add_argument('-v', '--verbosity', default=0, action='count') + return parser.parse_args() + + +def RunCommand(cmd, stream_stdout=False): + """Runs the command line and streams stdout if requested.""" + kwargs = { + 'args': cmd, + 'stderr': subprocess.PIPE, + } + if not stream_stdout: + kwargs['stdout'] = subprocess.PIPE + + p = subprocess.Popen(**kwargs) + stdout, stderr = p.communicate() + if p.returncode: + raise Error(stderr) + if not stream_stdout: + logging.debug(stdout) + return stdout + + +def Archive(isolated, isolate_server=None): + """Calls isolate.py archive with the given args.""" + cmd = [ + sys.executable, + ISOLATE_PY, + 'archive', + '--isolated', isolated, + ] + if isolate_server: + cmd.extend(['--isolate-server', isolate_server]) + print ' '.join(cmd) + return RunCommand(cmd).split()[0] # The isolated hash + + +def GetSwarmingCommandLine(args): + """Builds and returns the command line for swarming.py run|trigger.""" + cmd = [ + sys.executable, + SWARMING_PY, + args.action, + args.controller_isolated, + ] + if args.isolate_server: + cmd.extend(['--isolate-server', args.isolate_server]) + if args.swarming_server: + cmd.extend(['--swarming', args.swarming_server]) + if args.task_name: + cmd.extend(['--task-name', args.task_name]) + # swarming.py dimensions + for name, value in args.dimensions: + cmd.extend(['--dimension', name, value]) + + cmd.append('--') + + # Task name/hash values + for name, isolated in args.tasks: + cmd.extend(['--' + name, Archive(isolated, args.isolate_server)]) + # Test controller args + for name, value in args.controller_vars: + cmd.extend(['--' + name, value]) + print ' '.join(cmd) + return cmd + + +def main(): + args = GetArgs() + logging.basicConfig( + format='%(asctime)s %(filename)s:%(lineno)s %(levelname)s] %(message)s', + datefmt='%H:%M:%S', + level=LOGGING_LEVELS[len(LOGGING_LEVELS)-args.verbosity-1]) + cmd = GetSwarmingCommandLine(args) + if not args.format_only: + RunCommand(cmd, True) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/testing/multiprocess_func_list.cc b/engine/src/flutter/testing/multiprocess_func_list.cc new file mode 100644 index 0000000000..49ae07dd3e --- /dev/null +++ b/engine/src/flutter/testing/multiprocess_func_list.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2012 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. + +#include "multiprocess_func_list.h" + +#include + +// Helper functions to maintain mapping of "test name"->test func. +// The information is accessed via a global map. +namespace multi_process_function_list { + +namespace { + +struct ProcessFunctions { + ProcessFunctions() : main(NULL), setup(NULL) {} + ProcessFunctions(TestMainFunctionPtr main, SetupFunctionPtr setup) + : main(main), + setup(setup) { + } + TestMainFunctionPtr main; + SetupFunctionPtr setup; +}; + +typedef std::map MultiProcessTestMap; + +// Retrieve a reference to the global 'func name' -> func ptr map. +MultiProcessTestMap& GetMultiprocessFuncMap() { + static MultiProcessTestMap test_name_to_func_ptr_map; + return test_name_to_func_ptr_map; +} + +} // namespace + +AppendMultiProcessTest::AppendMultiProcessTest( + std::string test_name, + TestMainFunctionPtr main_func_ptr, + SetupFunctionPtr setup_func_ptr) { + GetMultiprocessFuncMap()[test_name] = + ProcessFunctions(main_func_ptr, setup_func_ptr); +} + +int InvokeChildProcessTest(std::string test_name) { + MultiProcessTestMap& func_lookup_table = GetMultiprocessFuncMap(); + MultiProcessTestMap::iterator it = func_lookup_table.find(test_name); + if (it != func_lookup_table.end()) { + const ProcessFunctions& process_functions = it->second; + if (process_functions.setup) + (*process_functions.setup)(); + if (process_functions.main) + return (*process_functions.main)(); + } + + return -1; +} + +} // namespace multi_process_function_list diff --git a/engine/src/flutter/testing/multiprocess_func_list.h b/engine/src/flutter/testing/multiprocess_func_list.h new file mode 100644 index 0000000000..f806d53c93 --- /dev/null +++ b/engine/src/flutter/testing/multiprocess_func_list.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012 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. + +#ifndef TESTING_MULTIPROCESS_FUNC_LIST_H_ +#define TESTING_MULTIPROCESS_FUNC_LIST_H_ + +#include + +// This file provides the plumbing to register functions to be executed +// as the main function of a child process in a multi-process test. +// This complements the MultiProcessTest class which provides facilities +// for launching such tests. +// +// The MULTIPROCESS_TEST_MAIN() macro registers a string -> func_ptr mapping +// by creating a new global instance of the AppendMultiProcessTest() class +// this means that by the time that we reach our main() function the mapping +// is already in place. +// +// Example usage: +// MULTIPROCESS_TEST_MAIN(a_test_func) { +// // Code here runs in a child process. +// return 0; +// } +// +// The prototype of a_test_func is implicitly +// int test_main_func_name(); + +namespace multi_process_function_list { + +// Type for child process main functions. +typedef int (*TestMainFunctionPtr)(); + +// Type for child setup functions. +typedef void (*SetupFunctionPtr)(); + +// Helper class to append a test function to the global mapping. +// Used by the MULTIPROCESS_TEST_MAIN macro. +class AppendMultiProcessTest { + public: + // |main_func_ptr| is the main function that is run in the child process. + // |setup_func_ptr| is a function run when the global mapping is added. + AppendMultiProcessTest(std::string test_name, + TestMainFunctionPtr main_func_ptr, + SetupFunctionPtr setup_func_ptr); +}; + +// Invoke the main function of a test previously registered with +// MULTIPROCESS_TEST_MAIN() +int InvokeChildProcessTest(std::string test_name); + +// This macro creates a global MultiProcessTest::AppendMultiProcessTest object +// whose constructor does the work of adding the global mapping. +#define MULTIPROCESS_TEST_MAIN(test_main) \ + MULTIPROCESS_TEST_MAIN_WITH_SETUP(test_main, NULL) + +// Same as above but lets callers specify a setup method that is run in the +// child process, just before the main function is run. This facilitates +// adding a generic one-time setup function for multiple tests. +#define MULTIPROCESS_TEST_MAIN_WITH_SETUP(test_main, test_setup) \ + int test_main(); \ + namespace { \ + multi_process_function_list::AppendMultiProcessTest \ + AddMultiProcessTest##_##test_main(#test_main, (test_main), (test_setup)); \ + } \ + int test_main() + +} // namespace multi_process_function_list + +#endif // TESTING_MULTIPROCESS_FUNC_LIST_H_ diff --git a/engine/src/flutter/testing/perf/BUILD.gn b/engine/src/flutter/testing/perf/BUILD.gn new file mode 100644 index 0000000000..949f15abf1 --- /dev/null +++ b/engine/src/flutter/testing/perf/BUILD.gn @@ -0,0 +1,14 @@ +# Copyright 2014 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. + +source_set("perf") { + testonly = true + sources = [ + "perf_test.cc", + "perf_test.h", + ] + deps = [ + "//base", + ] +} diff --git a/engine/src/flutter/testing/perf/perf_test.cc b/engine/src/flutter/testing/perf/perf_test.cc new file mode 100644 index 0000000000..0d5abc01cb --- /dev/null +++ b/engine/src/flutter/testing/perf/perf_test.cc @@ -0,0 +1,204 @@ +// Copyright 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. + +#include "testing/perf/perf_test.h" + +#include + +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" + +namespace { + +std::string ResultsToString(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& values, + const std::string& prefix, + const std::string& suffix, + const std::string& units, + bool important) { + // <*>RESULT : = + // <*>RESULT : = {, } + // <*>RESULT : = [,value,value,...,] + return base::StringPrintf("%sRESULT %s%s: %s= %s%s%s %s\n", + important ? "*" : "", measurement.c_str(), modifier.c_str(), + trace.c_str(), prefix.c_str(), values.c_str(), suffix.c_str(), + units.c_str()); +} + +void PrintResultsImpl(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& values, + const std::string& prefix, + const std::string& suffix, + const std::string& units, + bool important) { + fflush(stdout); + printf("%s", ResultsToString(measurement, modifier, trace, values, + prefix, suffix, units, important).c_str()); + fflush(stdout); +} + +} // namespace + +namespace perf_test { + +void PrintResult(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + size_t value, + const std::string& units, + bool important) { + PrintResultsImpl(measurement, + modifier, + trace, + base::UintToString(static_cast(value)), + std::string(), + std::string(), + units, + important); +} + +void PrintResult(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + double value, + const std::string& units, + bool important) { + PrintResultsImpl(measurement, + modifier, + trace, + base::DoubleToString(value), + std::string(), + std::string(), + units, + important); +} + +void AppendResult(std::string& output, + const std::string& measurement, + const std::string& modifier, + const std::string& trace, + size_t value, + const std::string& units, + bool important) { + output += ResultsToString( + measurement, + modifier, + trace, + base::UintToString(static_cast(value)), + std::string(), + std::string(), + units, + important); +} + +void PrintResult(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& value, + const std::string& units, + bool important) { + PrintResultsImpl(measurement, + modifier, + trace, + value, + std::string(), + std::string(), + units, + important); +} + +void AppendResult(std::string& output, + const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& value, + const std::string& units, + bool important) { + output += ResultsToString(measurement, + modifier, + trace, + value, + std::string(), + std::string(), + units, + important); +} + +void PrintResultMeanAndError(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& mean_and_error, + const std::string& units, + bool important) { + PrintResultsImpl(measurement, modifier, trace, mean_and_error, + "{", "}", units, important); +} + +void AppendResultMeanAndError(std::string& output, + const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& mean_and_error, + const std::string& units, + bool important) { + output += ResultsToString(measurement, modifier, trace, mean_and_error, + "{", "}", units, important); +} + +void PrintResultList(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& values, + const std::string& units, + bool important) { + PrintResultsImpl(measurement, modifier, trace, values, + "[", "]", units, important); +} + +void AppendResultList(std::string& output, + const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& values, + const std::string& units, + bool important) { + output += ResultsToString(measurement, modifier, trace, values, + "[", "]", units, important); +} + +void PrintSystemCommitCharge(const std::string& test_name, + size_t charge, + bool important) { + PrintSystemCommitCharge(stdout, test_name, charge, important); +} + +void PrintSystemCommitCharge(FILE* target, + const std::string& test_name, + size_t charge, + bool important) { + fprintf(target, "%s", SystemCommitChargeToString(test_name, charge, + important).c_str()); +} + +std::string SystemCommitChargeToString(const std::string& test_name, + size_t charge, + bool important) { + std::string trace_name(test_name); + std::string output; + AppendResult(output, + "commit_charge", + std::string(), + "cc" + trace_name, + charge, + "kb", + important); + return output; +} + +} // namespace perf_test diff --git a/engine/src/flutter/testing/perf/perf_test.gyp b/engine/src/flutter/testing/perf/perf_test.gyp new file mode 100644 index 0000000000..2065270c11 --- /dev/null +++ b/engine/src/flutter/testing/perf/perf_test.gyp @@ -0,0 +1,18 @@ +# Copyright 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. + +{ + 'targets': [ + { + 'target_name': 'perf_test', + 'type': 'static_library', + 'sources': [ + 'perf_test.cc', + ], + 'dependencies': [ + '../../base/base.gyp:base', + ], + }, + ], +} diff --git a/engine/src/flutter/testing/perf/perf_test.h b/engine/src/flutter/testing/perf/perf_test.h new file mode 100644 index 0000000000..36e2916c5f --- /dev/null +++ b/engine/src/flutter/testing/perf/perf_test.h @@ -0,0 +1,116 @@ +// Copyright 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. + +#ifndef TESTING_PERF_PERF_TEST_H_ +#define TESTING_PERF_PERF_TEST_H_ + +#include + +namespace perf_test { + +// Prints numerical information to stdout in a controlled format, for +// post-processing. |measurement| is a description of the quantity being +// measured, e.g. "vm_peak"; |modifier| is provided as a convenience and +// will be appended directly to the name of the |measurement|, e.g. +// "_browser"; |trace| is a description of the particular data point, e.g. +// "reference"; |value| is the measured value; and |units| is a description +// of the units of measure, e.g. "bytes". If |important| is true, the output +// line will be specially marked, to notify the post-processor. The strings +// may be empty. They should not contain any colons (:) or equals signs (=). +// A typical post-processing step would be to produce graphs of the data +// produced for various builds, using the combined |measurement| + |modifier| +// string to specify a particular graph and the |trace| to identify a trace +// (i.e., data series) on that graph. +void PrintResult(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + size_t value, + const std::string& units, + bool important); +void PrintResult(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + double value, + const std::string& units, + bool important); + +void AppendResult(std::string& output, + const std::string& measurement, + const std::string& modifier, + const std::string& trace, + size_t value, + const std::string& units, + bool important); + +// Like the above version of PrintResult(), but takes a std::string value +// instead of a size_t. +void PrintResult(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& value, + const std::string& units, + bool important); + +void AppendResult(std::string& output, + const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& value, + const std::string& units, + bool important); + +// Like PrintResult(), but prints a (mean, standard deviation) result pair. +// The || should be two comma-separated numbers, the mean and +// standard deviation (or other error metric) of the measurement. +void PrintResultMeanAndError(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& mean_and_error, + const std::string& units, + bool important); + +void AppendResultMeanAndError(std::string& output, + const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& mean_and_error, + const std::string& units, + bool important); + +// Like PrintResult(), but prints an entire list of results. The |values| +// will generally be a list of comma-separated numbers. A typical +// post-processing step might produce plots of their mean and standard +// deviation. +void PrintResultList(const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& values, + const std::string& units, + bool important); + +void AppendResultList(std::string& output, + const std::string& measurement, + const std::string& modifier, + const std::string& trace, + const std::string& values, + const std::string& units, + bool important); + +// Prints memory commit charge stats for use by perf graphs. +void PrintSystemCommitCharge(const std::string& test_name, + size_t charge, + bool important); + +void PrintSystemCommitCharge(FILE* target, + const std::string& test_name, + size_t charge, + bool important); + +std::string SystemCommitChargeToString(const std::string& test_name, + size_t charge, + bool important); + +} // namespace perf_test + +#endif // TESTING_PERF_PERF_TEST_H_ diff --git a/engine/src/flutter/testing/platform_test.h b/engine/src/flutter/testing/platform_test.h new file mode 100644 index 0000000000..04fc845bd9 --- /dev/null +++ b/engine/src/flutter/testing/platform_test.h @@ -0,0 +1,36 @@ +// Copyright (c) 2006-2008 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. + +#ifndef TESTING_PLATFORM_TEST_H_ +#define TESTING_PLATFORM_TEST_H_ + +#include + +#if defined(GTEST_OS_MAC) +#ifdef __OBJC__ +@class NSAutoreleasePool; +#else +class NSAutoreleasePool; +#endif + +// The purpose of this class us to provide a hook for platform-specific +// operations across unit tests. For example, on the Mac, it creates and +// releases an outer NSAutoreleasePool for each test case. For now, it's only +// implemented on the Mac. To enable this for another platform, just adjust +// the #ifdefs and add a platform_test_.cc implementation file. +class PlatformTest : public testing::Test { + public: + virtual ~PlatformTest(); + + protected: + PlatformTest(); + + private: + NSAutoreleasePool* pool_; +}; +#else +typedef testing::Test PlatformTest; +#endif // GTEST_OS_MAC + +#endif // TESTING_PLATFORM_TEST_H_ diff --git a/engine/src/flutter/testing/platform_test_mac.mm b/engine/src/flutter/testing/platform_test_mac.mm new file mode 100644 index 0000000000..bd22cd5b45 --- /dev/null +++ b/engine/src/flutter/testing/platform_test_mac.mm @@ -0,0 +1,15 @@ +// Copyright (c) 2006-2008 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. + +#include "platform_test.h" + +#import + +PlatformTest::PlatformTest() + : pool_([[NSAutoreleasePool alloc] init]) { +} + +PlatformTest::~PlatformTest() { + [pool_ release]; +} diff --git a/engine/src/flutter/testing/scripts/OWNERS b/engine/src/flutter/testing/scripts/OWNERS new file mode 100644 index 0000000000..9f378a2642 --- /dev/null +++ b/engine/src/flutter/testing/scripts/OWNERS @@ -0,0 +1,5 @@ +# This is needed because of * in testing/OWNERS . +set noparent + +iannucci@chromium.org +phajdan.jr@chromium.org diff --git a/engine/src/flutter/testing/scripts/checkdeps.py b/engine/src/flutter/testing/scripts/checkdeps.py new file mode 100755 index 0000000000..d6140dac12 --- /dev/null +++ b/engine/src/flutter/testing/scripts/checkdeps.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + with common.temporary_file() as tempfile_path: + rc = common.run_command([ + os.path.join(common.SRC_DIR, 'buildtools', 'checkdeps', 'checkdeps.py'), + '--json', tempfile_path + ]) + + with open(tempfile_path) as f: + checkdeps_results = json.load(f) + + result_set = set() + for result in checkdeps_results: + for violation in result['violations']: + result_set.add((result['dependee_path'], violation['include_path'])) + + json.dump({ + 'valid': True, + 'failures': ['%s: %s' % (r[0], r[1]) for r in result_set], + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump([], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/checklicenses.py b/engine/src/flutter/testing/scripts/checklicenses.py new file mode 100755 index 0000000000..43342d0254 --- /dev/null +++ b/engine/src/flutter/testing/scripts/checklicenses.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + with common.temporary_file() as tempfile_path: + rc = common.run_command([ + os.path.join(common.SRC_DIR, 'tools', 'checklicenses', + 'checklicenses.py'), + '--json', tempfile_path + ]) + + with open(tempfile_path) as f: + checklicenses_results = json.load(f) + + result_set = set() + for result in checklicenses_results: + result_set.add((result['filename'], result['license'])) + + json.dump({ + 'valid': True, + 'failures': ['%s: %s' % (r[0], r[1]) for r in result_set], + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump([], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/checkperms.py b/engine/src/flutter/testing/scripts/checkperms.py new file mode 100755 index 0000000000..17e32c4f62 --- /dev/null +++ b/engine/src/flutter/testing/scripts/checkperms.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + with common.temporary_file() as tempfile_path: + rc = common.run_command([ + os.path.join(common.SRC_DIR, 'tools', 'checkperms', 'checkperms.py'), + '--root', args.paths['checkout'], + '--json', tempfile_path + ]) + + with open(tempfile_path) as f: + checkperms_results = json.load(f) + + result_set = set() + for result in checkperms_results: + result_set.add((result['rel_path'], result['error'])) + + json.dump({ + 'valid': True, + 'failures': ['%s: %s' % (r[0], r[1]) for r in result_set], + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump([], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/common.py b/engine/src/flutter/testing/scripts/common.py new file mode 100644 index 0000000000..1aefcd5b79 --- /dev/null +++ b/engine/src/flutter/testing/scripts/common.py @@ -0,0 +1,139 @@ +# Copyright 2014 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. + +import argparse +import contextlib +import json +import os +import subprocess +import sys +import tempfile + + +SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) +SRC_DIR = os.path.abspath( + os.path.join(SCRIPT_DIR, os.path.pardir, os.path.pardir)) + + +# run-webkit-tests returns the number of failures as the return +# code, but caps the return code at 101 to avoid overflow or colliding +# with reserved values from the shell. +MAX_FAILURES_EXIT_STATUS = 101 + + +def run_script(argv, funcs): + def parse_json(path): + with open(path) as f: + return json.load(f) + parser = argparse.ArgumentParser() + # TODO(phajdan.jr): Make build-config-fs required after passing it in recipe. + parser.add_argument('--build-config-fs') + parser.add_argument('--paths', type=parse_json, default={}) + # Properties describe the environment of the build, and are the same per + # script invocation. + parser.add_argument('--properties', type=parse_json, default={}) + # Args contains per-invocation arguments that potentially change the + # behavior of the script. + parser.add_argument('--args', type=parse_json, default=[]) + + subparsers = parser.add_subparsers() + + run_parser = subparsers.add_parser('run') + run_parser.add_argument( + '--output', type=argparse.FileType('w'), required=True) + run_parser.add_argument('--filter-file', type=argparse.FileType('r')) + run_parser.set_defaults(func=funcs['run']) + + run_parser = subparsers.add_parser('compile_targets') + run_parser.add_argument( + '--output', type=argparse.FileType('w'), required=True) + run_parser.set_defaults(func=funcs['compile_targets']) + + args = parser.parse_args(argv) + return args.func(args) + + +def run_command(argv): + print 'Running %r' % argv + rc = subprocess.call(argv) + print 'Command %r returned exit code %d' % (argv, rc) + return rc + + +def run_runtest(cmd_args, runtest_args): + return run_command([ + sys.executable, + os.path.join(cmd_args.paths['build'], 'scripts', 'tools', 'runit.py'), + '--show-path', + sys.executable, + os.path.join(cmd_args.paths['build'], 'scripts', 'slave', 'runtest.py'), + '--target', cmd_args.build_config_fs, + '--xvfb', + '--builder-name', cmd_args.properties['buildername'], + '--slave-name', cmd_args.properties['slavename'], + '--build-number', str(cmd_args.properties['buildnumber']), + '--build-properties', json.dumps(cmd_args.properties), + ] + runtest_args) + + +@contextlib.contextmanager +def temporary_file(): + fd, path = tempfile.mkstemp() + os.close(fd) + try: + yield path + finally: + os.remove(path) + + +def parse_common_test_results(json_results, test_separator='/'): + def convert_trie_to_flat_paths(trie, prefix=None): + # Also see webkitpy.layout_tests.layout_package.json_results_generator + result = {} + for name, data in trie.iteritems(): + if prefix: + name = prefix + test_separator + name + if len(data) and not 'actual' in data and not 'expected' in data: + result.update(convert_trie_to_flat_paths(data, name)) + else: + result[name] = data + return result + + results = { + 'passes': {}, + 'unexpected_passes': {}, + 'failures': {}, + 'unexpected_failures': {}, + 'flakes': {}, + 'unexpected_flakes': {}, + } + + # TODO(dpranke): crbug.com/357866 - we should simplify the handling of + # both the return code and parsing the actual results, below. + + passing_statuses = ('PASS', 'SLOW', 'NEEDSREBASELINE', + 'NEEDSMANUALREBASELINE') + + for test, result in convert_trie_to_flat_paths( + json_results['tests']).iteritems(): + key = 'unexpected_' if result.get('is_unexpected') else '' + data = result['actual'] + actual_results = data.split() + last_result = actual_results[-1] + expected_results = result['expected'].split() + + if (len(actual_results) > 1 and + (last_result in expected_results or last_result in passing_statuses)): + key += 'flakes' + elif last_result in passing_statuses: + key += 'passes' + # TODO(dpranke): crbug.com/357867 ... Why are we assigning result + # instead of actual_result here. Do we even need these things to be + # hashes, or just lists? + data = result + else: + key += 'failures' + results[key][test] = data + + return results diff --git a/engine/src/flutter/testing/scripts/get_compile_targets.py b/engine/src/flutter/testing/scripts/get_compile_targets.py new file mode 100755 index 0000000000..16cff95164 --- /dev/null +++ b/engine/src/flutter/testing/scripts/get_compile_targets.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +import argparse +import json +import os +import sys + +import common + + +def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument('--output', required=True) + parser.add_argument('args', nargs=argparse.REMAINDER) + + args = parser.parse_args(argv) + + passthrough_args = args.args + if passthrough_args[0] == '--': + passthrough_args = passthrough_args[1:] + + results = {} + + for filename in os.listdir(common.SCRIPT_DIR): + if not filename.endswith('.py'): + continue + if filename in ('common.py', 'get_compile_targets.py'): + continue + + with common.temporary_file() as tempfile_path: + rc = common.run_command( + [sys.executable, os.path.join(common.SCRIPT_DIR, filename)] + + passthrough_args + + [ + 'compile_targets', + '--output', tempfile_path + ] + ) + if rc != 0: + return rc + + with open(tempfile_path) as f: + results[filename] = json.load(f) + + with open(args.output, 'w') as f: + json.dump(results, f) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/testing/scripts/gn_check.py b/engine/src/flutter/testing/scripts/gn_check.py new file mode 100755 index 0000000000..5feb166c5e --- /dev/null +++ b/engine/src/flutter/testing/scripts/gn_check.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +"""Wrap `gn check` for the bots. + +This script wraps the `gn check` command in the facade needed for the +'ScriptTest' step class of the chromium recipe_module +(see scripts/slave/recipe_modules/chromium/steps.py in the build repo). + +The script takes no arguments. +""" + + +import json +import os +import sys + + +import common + + +def main_run(args): + if sys.platform == 'win32': + exe = os.path.join(common.SRC_DIR, 'buildtools', 'win', 'gn.exe') + elif sys.platform == 'mac': + exe = os.path.join(common.SRC_DIR, 'buildtools', 'mac', 'gn') + else: + exe = os.path.join(common.SRC_DIR, 'buildtools', 'linux64', 'gn') + + rc = common.run_command([ + exe, + '--root=%s' % common.SRC_DIR, + 'check', + '//out/%s' % args.build_config_fs, + ]) + + # TODO(dpranke): Figure out how to get a list of failures out of gn check? + json.dump({ + 'valid': True, + 'failures': ['check_failed'] if rc else [], + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump([], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/gtest_perf_test.py b/engine/src/flutter/testing/scripts/gtest_perf_test.py new file mode 100755 index 0000000000..af4380f657 --- /dev/null +++ b/engine/src/flutter/testing/scripts/gtest_perf_test.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import json +import os +import sys + + +import common + + +def IsWindows(): + return sys.platform == 'cygwin' or sys.platform.startswith('win') + + +def main_run(args): + filter_tests = [] + if args.filter_file: + filter_tests = json.load(args.filter_file) + + perf_id = args.properties.get('perf-id') + script_args = args.args + test_suite = script_args[0] + if IsWindows(): + script_args[0] += '.exe' + + with common.temporary_file() as tempfile_path: + gtest_args = [ + '--target', args.build_config_fs, + '--annotate', 'graphing', + '--perf-id', perf_id, + '--perf-dashboard-id', test_suite, + '--results-url', args.properties.get('results-url'), + '--slave-name', args.properties.get('slavename'), + '--builder-name', args.properties.get('buildername'), + '--build-number', str(args.properties.get('buildnumber')), + '--log-processor-output-file', tempfile_path, + '--test-type', test_suite, + ] + + if 'android' == args.properties.get('target_platform'): + gtest_args.extend([ + '--no-xvfb', + '--run-python-script', os.path.join( + args.paths['checkout'], 'build', 'android', 'test_runner.py'), + 'gtest', '--release', + '--suite', test_suite, + '--verbose', + ]) + else: + gtest_args.extend(['--xvfb']) + gtest_args.extend(script_args) + + rc = common.run_runtest(args, gtest_args + filter_tests) + + with open(tempfile_path) as f: + results = json.load(f) + + json.dump({ + 'valid': bool(rc == 0), + 'failures': results['failed'], + }, args.output) + + return rc + + +def main_compile_targets(args): + if 'android' == args.properties.get('target_platform'): + json.dump(['${name}_apk'], args.output) + else: + json.dump(['$name'], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/gyp_flag_compare.py b/engine/src/flutter/testing/scripts/gyp_flag_compare.py new file mode 100755 index 0000000000..0b777d9adb --- /dev/null +++ b/engine/src/flutter/testing/scripts/gyp_flag_compare.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +"""Wrap //tools/gn/bin/gyp_flag_compare.py for the bots. + +This script wraps the GN test script in the facade needed for the +'ScriptTest' step class of the chromium recipe_module +(see scripts/slave/recipe_modules/chromium/steps.py in the build repo. + +The script takes N arguments, for the N targets to compare flags for. +""" + +import json +import os +import sys + + +import common + + +def main_run(args): + rc = common.run_command([sys.executable, + os.path.join(common.SRC_DIR, + 'tools', 'gn', 'bin', + 'gyp_flag_compare.py')] + args.args) + + # TODO(dpranke): Figure out how to get a list of failures out of + # gyp_flag_compare? + json.dump({ + 'valid': True, + 'failures': ['compare_failed'] if rc else [], + }, args.output) + + return rc + + +def main_compile_targets(args): + # TODO(dpranke): Figure out how to get args.args plumbed through to here. + json.dump([], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/host_info.py b/engine/src/flutter/testing/scripts/host_info.py new file mode 100755 index 0000000000..b3131d8fb5 --- /dev/null +++ b/engine/src/flutter/testing/scripts/host_info.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import json +import multiprocessing +import os +import platform +import subprocess +import sys + + +import common + + +def is_linux(): + return sys.platform.startswith('linux') + + +def get_free_disk_space(failures): + """Returns the amount of free space on the current disk, in GiB. + + Returns: + The amount of free space on the current disk, measured in GiB. + """ + if os.name == 'posix': + # Stat the current path for info on the current disk. + stat_result = os.statvfs('.') + # Multiply block size by number of free blocks, express in GiB. + return stat_result.f_frsize * stat_result.f_bavail / ( + 1024.0 / 1024.0 / 1024.0) + + failures.append('get_free_disk_space: OS %s not supported.' % os.name) + return 0 + + +def get_num_cpus(failures): + """Returns the number of logical CPUs on this machine. + + Returns: + The number of logical CPUs on this machine, or 'unknown' if indeterminate. + """ + try: + return multiprocessing.cpu_count() + except NotImplementedError: + failures.append('get_num_cpus') + return 'unknown' + + +def get_device_info(args, failures): + """Parses the device info for each attached device, and returns a summary + of the device info and any mismatches. + + Returns: + A dict indicating the result. + """ + if not is_linux(): + return {} + + with common.temporary_file() as tempfile_path: + rc = common.run_command([ + sys.executable, + os.path.join(args.paths['checkout'], + 'build', + 'android', + 'buildbot', + 'bb_device_status_check.py'), + '--json-output', tempfile_path]) + + if rc: + failures.append('bb_device_status_check') + return {} + + with open(tempfile_path, 'r') as src: + device_info = json.load(src) + + results = {} + results['devices'] = sorted(v['serial'] for v in device_info) + + details = [v['build_detail'] for v in device_info] + + def unique_build_details(index): + return sorted(list(set([v.split(':')[index] for v in details]))) + + parsed_details = { + 'device_names': unique_build_details(0), + 'build_versions': unique_build_details(1), + 'build_types': unique_build_details(2), + } + + for k, v in parsed_details.iteritems(): + if len(v) == 1: + results[k] = v[0] + else: + results[k] = 'MISMATCH' + results['%s_list' % k] = v + failures.append(k) + + return results + + +def main_run(args): + failures = [] + host_info = {} + host_info['os_system'] = platform.system() + host_info['os_release'] = platform.release() + + host_info['processor'] = platform.processor() + host_info['num_cpus'] = get_num_cpus(failures) + host_info['free_disk_space'] = get_free_disk_space(failures) + + host_info['python_version'] = platform.python_version() + host_info['python_path'] = sys.executable + + host_info['devices'] = get_device_info(args, failures) + + json.dump({ + 'valid': True, + 'failures': failures, + '_host_info': host_info, + }, args.output) + + return len(failures) != 0 + + +def main_compile_targets(args): + json.dump([], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/mojo_apptest.py b/engine/src/flutter/testing/scripts/mojo_apptest.py new file mode 100755 index 0000000000..09e3f234fd --- /dev/null +++ b/engine/src/flutter/testing/scripts/mojo_apptest.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + runner = os.path.join(common.SRC_DIR, 'mojo', 'tools', 'apptest_runner.py') + tests = os.path.join(common.SRC_DIR, 'mojo', 'tools', 'data', 'apptests') + build_dir = os.path.join(common.SRC_DIR, 'out', args.build_config_fs) + + with common.temporary_file() as tempfile_path: + rc = common.run_command([runner, tests, build_dir, '--verbose', + '--write-full-results-to', tempfile_path]) + with open(tempfile_path) as f: + results = json.load(f) + + parsed_results = common.parse_common_test_results(results, test_separator='.') + failures = parsed_results['unexpected_failures'] + + json.dump({ + 'valid': bool(rc <= common.MAX_FAILURES_EXIT_STATUS and + ((rc == 0) or failures)), + 'failures': failures.keys(), + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump(['mandoline:tests'], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/nacl_integration.py b/engine/src/flutter/testing/scripts/nacl_integration.py new file mode 100755 index 0000000000..f308b78b1c --- /dev/null +++ b/engine/src/flutter/testing/scripts/nacl_integration.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + filter_tests = [] + if args.filter_file: + filter_tests = json.load(args.filter_file) + + with common.temporary_file() as tempfile_path: + rc = common.run_command([ + sys.executable, + os.path.join(common.SRC_DIR, 'chrome', 'test', 'nacl_test_injection', + 'buildbot_nacl_integration.py'), + '--mode', args.build_config_fs, + '--json_build_results_output_file', tempfile_path, + ] + filter_tests) + + with open(tempfile_path) as f: + results = json.load(f) + + + json.dump({ + 'valid': True, + 'failures': [f['raw_name'] for f in results], + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump(['chrome'], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/telemetry_perf_unittests.py b/engine/src/flutter/testing/scripts/telemetry_perf_unittests.py new file mode 100755 index 0000000000..06e018136c --- /dev/null +++ b/engine/src/flutter/testing/scripts/telemetry_perf_unittests.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + filter_tests = [] + if args.filter_file: + filter_tests = json.load(args.filter_file) + + test_args = ['--retry-limit', '3'] + if 'android' == args.properties.get('target_platform'): + test_args += ['--browser', 'android-chrome-shell', '--device', 'android'] + else: + test_args += ['--browser', args.build_config_fs.lower()] + + with common.temporary_file() as tempfile_path: + test_args += ['--write-full-results-to', tempfile_path] + rc = common.run_runtest(args, [ + '--annotate', 'gtest', + '--test-type', 'telemetry_perf_unittests', + '--run-python-script', + os.path.join(common.SRC_DIR, 'tools', 'perf', 'run_tests') + ] + test_args + filter_tests) + + with open(tempfile_path) as f: + results = json.load(f) + + parsed_results = common.parse_common_test_results(results, test_separator='.') + failures = parsed_results['unexpected_failures'] + + json.dump({ + 'valid': bool(rc <= common.MAX_FAILURES_EXIT_STATUS and + ((rc == 0) or failures)), + 'failures': failures.keys(), + }, args.output) + + return rc + + +def main_compile_targets(args): + if 'android' == args.properties.get('target_platform'): + json.dump(['chrome_shell_apk'], args.output) + else: + json.dump(['chrome'], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/telemetry_unittests.py b/engine/src/flutter/testing/scripts/telemetry_unittests.py new file mode 100755 index 0000000000..50af0c08bf --- /dev/null +++ b/engine/src/flutter/testing/scripts/telemetry_unittests.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + filter_tests = [] + if args.filter_file: + filter_tests = json.load(args.filter_file) + + with common.temporary_file() as tempfile_path: + rc = common.run_runtest(args, [ + '--annotate', 'gtest', + '--test-type', 'telemetry_unittests', + '--run-python-script', + os.path.join(common.SRC_DIR, 'tools', 'telemetry', 'run_tests'), + '--browser', args.build_config_fs.lower(), + '--retry-limit', '3', + '--write-full-results-to', tempfile_path, + ] + filter_tests) + + with open(tempfile_path) as f: + results = json.load(f) + + parsed_results = common.parse_common_test_results(results, test_separator='.') + failures = parsed_results['unexpected_failures'] + + json.dump({ + 'valid': bool(rc <= common.MAX_FAILURES_EXIT_STATUS and + ((rc == 0) or failures)), + 'failures': failures.keys(), + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump(['chrome'], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/webkit_lint.py b/engine/src/flutter/testing/scripts/webkit_lint.py new file mode 100755 index 0000000000..eeb9367099 --- /dev/null +++ b/engine/src/flutter/testing/scripts/webkit_lint.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + with common.temporary_file() as tempfile_path: + rc = common.run_command([ + sys.executable, + os.path.join(common.SRC_DIR, 'third_party', 'WebKit', + 'Tools', 'Scripts', 'lint-test-expectations'), + '--json', tempfile_path + ]) + + with open(tempfile_path) as f: + failures = json.load(f) + + json.dump({ + 'valid': True, + 'failures': failures, + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump([], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/webkit_python_tests.py b/engine/src/flutter/testing/scripts/webkit_python_tests.py new file mode 100755 index 0000000000..b0c2fbc1c7 --- /dev/null +++ b/engine/src/flutter/testing/scripts/webkit_python_tests.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + with common.temporary_file() as tempfile_path: + rc = common.run_command([ + sys.executable, + os.path.join(common.SRC_DIR, 'third_party', 'WebKit', + 'Tools', 'Scripts', 'test-webkitpy'), + '--write-full-results-to', tempfile_path, + ]) + + with open(tempfile_path) as f: + results = json.load(f) + + parsed_results = common.parse_common_test_results(results) + failures = parsed_results['unexpected_failures'] + + json.dump({ + 'valid': bool(rc <= common.MAX_FAILURES_EXIT_STATUS and + ((rc == 0) or failures)), + 'failures': failures.keys(), + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump([], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/scripts/webview_licenses.py b/engine/src/flutter/testing/scripts/webview_licenses.py new file mode 100755 index 0000000000..255c62e993 --- /dev/null +++ b/engine/src/flutter/testing/scripts/webview_licenses.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +import json +import os +import sys + + +import common + + +def main_run(args): + with common.temporary_file() as tempfile_path: + rc = common.run_command([ + os.path.join(common.SRC_DIR, 'android_webview', 'tools', + 'webview_licenses.py'), + 'scan', + '--json', tempfile_path + ]) + + with open(tempfile_path) as f: + results = json.load(f) + + json.dump({ + 'valid': True, + 'failures': results, + }, args.output) + + return rc + + +def main_compile_targets(args): + json.dump([], args.output) + + +if __name__ == '__main__': + funcs = { + 'run': main_run, + 'compile_targets': main_compile_targets, + } + sys.exit(common.run_script(sys.argv[1:], funcs)) diff --git a/engine/src/flutter/testing/test.gni b/engine/src/flutter/testing/test.gni new file mode 100644 index 0000000000..c99df9036b --- /dev/null +++ b/engine/src/flutter/testing/test.gni @@ -0,0 +1,256 @@ +# Copyright 2015 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. + +# ============================================================================== +# TEST SETUP +# ============================================================================== + +# Define a test as an executable (or apk on Android) with the "testonly" flag +# set. +template("test") { + if (is_android) { + import("//build/config/android/config.gni") + import("//build/config/android/rules.gni") + + main_target_name = target_name + library_name = "_${target_name}__library" + apk_name = "${target_name}_apk" + + shared_library(library_name) { + # Configs will always be defined since we set_defaults for a component + # in the main config. We want to use those rather than whatever came with + # the nested shared/static library inside the component. + configs = [] # Prevent list overwriting warning. + configs = invoker.configs + + # See above call. + set_sources_assignment_filter([]) + + testonly = true + + if (defined(invoker.all_dependent_configs)) { + all_dependent_configs = invoker.all_dependent_configs + } + if (defined(invoker.allow_circular_includes_from)) { + allow_circular_includes_from = invoker.allow_circular_includes_from + } + if (defined(invoker.cflags)) { + cflags = invoker.cflags + } + if (defined(invoker.cflags_c)) { + cflags_c = invoker.cflags_c + } + if (defined(invoker.cflags_cc)) { + cflags_cc = invoker.cflags_cc + } + if (defined(invoker.cflags_objc)) { + cflags_objc = invoker.cflags_objc + } + if (defined(invoker.cflags_objcc)) { + cflags_objcc = invoker.cflags_objcc + } + if (defined(invoker.check_includes)) { + check_includes = invoker.check_includes + } + if (defined(invoker.data)) { + data = invoker.data + } + if (defined(invoker.data_deps)) { + data_deps = invoker.data_deps + } + if (defined(invoker.datadeps)) { + datadeps = invoker.datadeps + } + if (defined(invoker.defines)) { + defines = invoker.defines + } + deps = [] + if (!defined(invoker.use_launcher) || invoker.use_launcher) { + deps += [ "//testing/android/native_test:native_test_native_code" ] + } + if (defined(invoker.deps)) { + deps += invoker.deps + } + if (defined(invoker.direct_dependent_configs)) { + direct_dependent_configs = invoker.direct_dependent_configs + } + if (defined(invoker.forward_dependent_configs_from)) { + forward_dependent_configs_from = invoker.forward_dependent_configs_from + } + if (defined(invoker.include_dirs)) { + include_dirs = invoker.include_dirs + } + if (defined(invoker.ldflags)) { + ldflags = invoker.ldflags + } + if (defined(invoker.lib_dirs)) { + lib_dirs = invoker.lib_dirs + } + if (defined(invoker.libs)) { + libs = invoker.libs + } + if (defined(invoker.output_extension)) { + output_extension = invoker.output_extension + } + if (defined(invoker.output_name)) { + output_name = invoker.output_name + } + if (defined(invoker.public)) { + public = invoker.public + } + if (defined(invoker.public_configs)) { + public_configs = invoker.public_configs + } + if (defined(invoker.public_deps)) { + public_deps = invoker.public_deps + } + if (defined(invoker.sources)) { + sources = invoker.sources + } + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + } + + unittest_apk(apk_name) { + unittests_dep = ":$library_name" + apk_name = main_target_name + if (defined(invoker.output_name)) { + apk_name = invoker.output_name + unittests_binary = "lib${apk_name}.so" + } + deps = [ + ":$library_name", + ] + if (defined(invoker.apk_deps)) { + deps += invoker.apk_deps + } + if (defined(invoker.apk_asset_location)) { + asset_location = invoker.apk_asset_location + } + } + + test_name = main_target_name + if (defined(invoker.output_name)) { + test_name = invoker.output_name + } + test_runner_script_name = "${test_name}__test_runner_script" + test_runner_script(test_runner_script_name) { + test_name = test_name + test_type = "gtest" + test_suite = test_name + if (defined(invoker.isolate_file)) { + isolate_file = invoker.isolate_file + } + } + + group(target_name) { + testonly = true + datadeps = [ + ":$test_runner_script_name", + ] + deps = [ + ":$library_name", + ":$apk_name", + ] + } + } else { + executable(target_name) { + # See above. + configs = [] # Prevent list overwriting warning. + configs = invoker.configs + + # See above call. + set_sources_assignment_filter([]) + + testonly = true + + if (defined(invoker.all_dependent_configs)) { + all_dependent_configs = invoker.all_dependent_configs + } + if (defined(invoker.allow_circular_includes_from)) { + allow_circular_includes_from = invoker.allow_circular_includes_from + } + if (defined(invoker.cflags)) { + cflags = invoker.cflags + } + if (defined(invoker.cflags_c)) { + cflags_c = invoker.cflags_c + } + if (defined(invoker.cflags_cc)) { + cflags_cc = invoker.cflags_cc + } + if (defined(invoker.cflags_objc)) { + cflags_objc = invoker.cflags_objc + } + if (defined(invoker.cflags_objcc)) { + cflags_objcc = invoker.cflags_objcc + } + if (defined(invoker.check_includes)) { + check_includes = invoker.check_includes + } + if (defined(invoker.data)) { + data = invoker.data + } + if (defined(invoker.data_deps)) { + data_deps = invoker.data_deps + } + if (defined(invoker.datadeps)) { + datadeps = invoker.datadeps + } + if (defined(invoker.defines)) { + defines = invoker.defines + } + + # All shared libraries must have the sanitizer deps to properly link in + # asan mode (this target will be empty in other cases). + if (defined(invoker.deps)) { + deps = invoker.deps + [ "//build/config/sanitizers:deps" ] + } else { + deps = [ + "//build/config/sanitizers:deps", + ] + } + if (defined(invoker.direct_dependent_configs)) { + direct_dependent_configs = invoker.direct_dependent_configs + } + if (defined(invoker.forward_dependent_configs_from)) { + forward_dependent_configs_from = invoker.forward_dependent_configs_from + } + if (defined(invoker.include_dirs)) { + include_dirs = invoker.include_dirs + } + if (defined(invoker.ldflags)) { + ldflags = invoker.ldflags + } + if (defined(invoker.lib_dirs)) { + lib_dirs = invoker.lib_dirs + } + if (defined(invoker.libs)) { + libs = invoker.libs + } + if (defined(invoker.output_extension)) { + output_extension = invoker.output_extension + } + if (defined(invoker.output_name)) { + output_name = invoker.output_name + } + if (defined(invoker.public)) { + public = invoker.public + } + if (defined(invoker.public_configs)) { + public_configs = invoker.public_configs + } + if (defined(invoker.public_deps)) { + public_deps = invoker.public_deps + } + if (defined(invoker.sources)) { + sources = invoker.sources + } + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + } + } +} diff --git a/engine/src/flutter/testing/test_env.py b/engine/src/flutter/testing/test_env.py new file mode 100755 index 0000000000..052df6769b --- /dev/null +++ b/engine/src/flutter/testing/test_env.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Sets environment variables needed to run a chromium unit test.""" + +import os +import stat +import subprocess +import sys + +# This is hardcoded to be src/ relative to this script. +ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +CHROME_SANDBOX_ENV = 'CHROME_DEVEL_SANDBOX' +CHROME_SANDBOX_PATH = '/opt/chromium/chrome_sandbox' + + +def get_sandbox_env(env): + """Returns the environment flags needed for the SUID sandbox to work.""" + extra_env = {} + chrome_sandbox_path = env.get(CHROME_SANDBOX_ENV, CHROME_SANDBOX_PATH) + # The above would silently disable the SUID sandbox if the env value were + # an empty string. We don't want to allow that. http://crbug.com/245376 + # TODO(jln): Remove this check once it's no longer possible to disable the + # sandbox that way. + if not chrome_sandbox_path: + chrome_sandbox_path = CHROME_SANDBOX_PATH + extra_env[CHROME_SANDBOX_ENV] = chrome_sandbox_path + + return extra_env + + +def trim_cmd(cmd): + """Removes internal flags from cmd since they're just used to communicate from + the host machine to this script running on the swarm slaves.""" + sanitizers = ['asan', 'lsan', 'msan', 'tsan'] + internal_flags = frozenset('--%s=%d' % (name, value) + for name in sanitizers + for value in [0, 1]) + return [i for i in cmd if i not in internal_flags] + + +def fix_python_path(cmd): + """Returns the fixed command line to call the right python executable.""" + out = cmd[:] + if out[0] == 'python': + out[0] = sys.executable + elif out[0].endswith('.py'): + out.insert(0, sys.executable) + return out + + +def get_sanitizer_env(cmd, asan, lsan, msan, tsan): + """Returns the envirnoment flags needed for sanitizer tools.""" + + extra_env = {} + + # Instruct GTK to use malloc while running sanitizer-instrumented tests. + extra_env['G_SLICE'] = 'always-malloc' + + extra_env['NSS_DISABLE_ARENA_FREE_LIST'] = '1' + extra_env['NSS_DISABLE_UNLOAD'] = '1' + + # TODO(glider): remove the symbolizer path once + # https://code.google.com/p/address-sanitizer/issues/detail?id=134 is fixed. + symbolizer_path = os.path.abspath(os.path.join(ROOT_DIR, 'third_party', + 'llvm-build', 'Release+Asserts', 'bin', 'llvm-symbolizer')) + + if lsan or tsan: + # LSan is not sandbox-compatible, so we can use online symbolization. In + # fact, it needs symbolization to be able to apply suppressions. + symbolization_options = ['symbolize=1', + 'external_symbolizer_path=%s' % symbolizer_path] + elif (asan or msan) and sys.platform not in ['win32', 'cygwin']: + # ASan uses a script for offline symbolization, except on Windows. + # Important note: when running ASan with leak detection enabled, we must use + # the LSan symbolization options above. + symbolization_options = ['symbolize=0'] + # Set the path to llvm-symbolizer to be used by asan_symbolize.py + extra_env['LLVM_SYMBOLIZER_PATH'] = symbolizer_path + else: + symbolization_options = [] + + if asan: + asan_options = symbolization_options[:] + if lsan: + asan_options.append('detect_leaks=1') + + if asan_options: + extra_env['ASAN_OPTIONS'] = ' '.join(asan_options) + + if sys.platform == 'darwin': + isolate_output_dir = os.path.abspath(os.path.dirname(cmd[0])) + # This is needed because the test binary has @executable_path embedded in + # it that the OS tries to resolve to the cache directory and not the + # mapped directory. + extra_env['DYLD_LIBRARY_PATH'] = str(isolate_output_dir) + + if lsan: + if asan or msan: + lsan_options = [] + else: + lsan_options = symbolization_options[:] + if sys.platform == 'linux2': + # Use the debug version of libstdc++ under LSan. If we don't, there will + # be a lot of incomplete stack traces in the reports. + extra_env['LD_LIBRARY_PATH'] = '/usr/lib/x86_64-linux-gnu/debug:' + + extra_env['LSAN_OPTIONS'] = ' '.join(lsan_options) + + if msan: + msan_options = symbolization_options[:] + if lsan: + msan_options.append('detect_leaks=1') + extra_env['MSAN_OPTIONS'] = ' '.join(msan_options) + + if tsan: + tsan_options = symbolization_options[:] + extra_env['TSAN_OPTIONS'] = ' '.join(tsan_options) + + return extra_env + + +def get_sanitizer_symbolize_command(json_path=None, executable_path=None): + """Construct the command to invoke offline symbolization script.""" + script_path = '../tools/valgrind/asan/asan_symbolize.py' + cmd = [sys.executable, script_path] + if json_path is not None: + cmd.append('--test-summary-json-file=%s' % json_path) + if executable_path is not None: + cmd.append('--executable-path=%s' % executable_path) + return cmd + + +def get_json_path(cmd): + """Extract the JSON test summary path from a command line.""" + json_path_flag = '--test-launcher-summary-output=' + for arg in cmd: + if arg.startswith(json_path_flag): + return arg.split(json_path_flag).pop() + return None + + +def symbolize_snippets_in_json(cmd, env): + """Symbolize output snippets inside the JSON test summary.""" + json_path = get_json_path(cmd) + if json_path is None: + return + + try: + symbolize_command = get_sanitizer_symbolize_command( + json_path=json_path, executable_path=cmd[0]) + p = subprocess.Popen(symbolize_command, stderr=subprocess.PIPE, env=env) + (_, stderr) = p.communicate() + except OSError as e: + print 'Exception while symbolizing snippets: %s' % e + + if p.returncode != 0: + print "Error: failed to symbolize snippets in JSON:\n" + print stderr + + +def run_executable(cmd, env): + """Runs an executable with: + - environment variable CR_SOURCE_ROOT set to the root directory. + - environment variable LANGUAGE to en_US.UTF-8. + - environment variable CHROME_DEVEL_SANDBOX set + - Reuses sys.executable automatically. + """ + extra_env = {} + # Many tests assume a English interface... + extra_env['LANG'] = 'en_US.UTF-8' + # Used by base/base_paths_linux.cc as an override. Just make sure the default + # logic is used. + env.pop('CR_SOURCE_ROOT', None) + extra_env.update(get_sandbox_env(env)) + + # Copy logic from tools/build/scripts/slave/runtest.py. + asan = '--asan=1' in cmd + lsan = '--lsan=1' in cmd + msan = '--msan=1' in cmd + tsan = '--tsan=1' in cmd + if sys.platform in ['win32', 'cygwin']: + # Symbolization works in-process on Windows even when sandboxed. + use_symbolization_script = False + else: + # LSan doesn't support sandboxing yet, so we use the in-process symbolizer. + # Note that ASan and MSan can work together with LSan. + use_symbolization_script = (asan or msan) and not lsan + + if asan or lsan or msan or tsan: + extra_env.update(get_sanitizer_env(cmd, asan, lsan, msan, tsan)) + + if lsan or tsan: + # LSan and TSan are not sandbox-friendly. + cmd.append('--no-sandbox') + + cmd = trim_cmd(cmd) + + # Ensure paths are correctly separated on windows. + cmd[0] = cmd[0].replace('/', os.path.sep) + cmd = fix_python_path(cmd) + + print('Additional test environment:\n%s\n' + 'Command: %s\n' % ( + '\n'.join(' %s=%s' % + (k, v) for k, v in sorted(extra_env.iteritems())), + ' '.join(cmd))) + env.update(extra_env or {}) + try: + # See above comment regarding offline symbolization. + if use_symbolization_script: + # Need to pipe to the symbolizer script. + p1 = subprocess.Popen(cmd, env=env, stdout=subprocess.PIPE, + stderr=sys.stdout) + p2 = subprocess.Popen( + get_sanitizer_symbolize_command(executable_path=cmd[0]), + env=env, stdin=p1.stdout) + p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits. + p1.wait() + p2.wait() + # Also feed the out-of-band JSON output to the symbolizer script. + symbolize_snippets_in_json(cmd, env) + return p1.returncode + else: + return subprocess.call(cmd, env=env) + except OSError: + print >> sys.stderr, 'Failed to start %s' % cmd + raise + + +def main(): + return run_executable(sys.argv[1:], os.environ.copy()) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/testing/variations/PRESUBMIT.py b/engine/src/flutter/testing/variations/PRESUBMIT.py new file mode 100644 index 0000000000..9e565e03d7 --- /dev/null +++ b/engine/src/flutter/testing/variations/PRESUBMIT.py @@ -0,0 +1,83 @@ +# Copyright 2015 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. + +"""Presubmit script validating field trial configs. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details on the presubmit API built into depot_tools. +""" + +def ValidateData(json_data, file_path, message_type): + """Validates the format of a fieldtrial configuration. + + Args: + json_data: Parsed JSON object representing the fieldtrial config. + file_path: String representing the path to the JSON file. + message_type: Type of message from |output_api| to return in the case of + errors/warnings. + + Returns: + A list of |message_type| messages. In the case of all tests passing with no + warnings/errors, this will return []. + """ + if not isinstance(json_data, dict): + return [message_type( + 'Malformed config file %s: Expecting dict' % file_path)] + for (study, groups) in json_data.iteritems(): + if not isinstance(study, unicode): + return [message_type( + 'Malformed config file %s: Expecting keys to be string, got %s' + % (file_path, type(study)))] + if not isinstance(groups, list): + return [message_type( + 'Malformed config file %s: Expecting list for study %s' + % (file_path, study))] + for group in groups: + if not isinstance(group, dict): + return [message_type( + 'Malformed config file %s: Expecting dict for group in ' + 'Study[%s]' % (file_path, study))] + if not 'group_name' in group or not isinstance(group['group_name'], + unicode): + return [message_type( + 'Malformed config file %s: Missing valid group_name for group' + ' in Study[%s]' % (file_path, study))] + if 'params' in group: + params = group['params'] + if not isinstance(params, dict): + return [message_type( + 'Malformed config file %s: Invalid params for Group[%s]' + ' in Study[%s]' % (file_path, group['group_name'], + study))] + for (key, value) in params.iteritems(): + if not isinstance(key, unicode) or not isinstance(value, + unicode): + return [message_type( + 'Malformed config file %s: Invalid params for Group[%s]' + ' in Study[%s]' % (file_path, group['group_name'], + study))] + return [] + +def CommonChecks(input_api, output_api): + affected_files = input_api.AffectedFiles( + include_deletes=False, + file_filter=lambda x: x.LocalPath().endswith('.json')) + for f in affected_files: + contents = input_api.ReadFile(f) + try: + json_data = input_api.json.loads(contents) + result = ValidateData(json_data, f.LocalPath(), + output_api.PresubmitError) + if len(result): + return result + except ValueError: + return [output_api.PresubmitError( + 'Malformed JSON file: %s' % f.LocalPath())] + return [] + +def CheckChangeOnUpload(input_api, output_api): + return CommonChecks(input_api, output_api) + +def CheckChangeOnCommit(input_api, output_api): + return CommonChecks(input_api, output_api) diff --git a/engine/src/flutter/testing/variations/fieldtrial_testing_config.json b/engine/src/flutter/testing/variations/fieldtrial_testing_config.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/engine/src/flutter/testing/variations/fieldtrial_testing_config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/engine/src/flutter/testing/variations/fieldtrial_testing_config_win.json b/engine/src/flutter/testing/variations/fieldtrial_testing_config_win.json new file mode 100644 index 0000000000..c80a14e1e2 --- /dev/null +++ b/engine/src/flutter/testing/variations/fieldtrial_testing_config_win.json @@ -0,0 +1,97 @@ +{ + "BrowserBlacklist": [ + { + "group_name": "Enabled" + } + ], + "CTRequiredForEVTrial": [ + { + "group_name": "RequirementEnforced" + } + ], + "ChildAccountDetection": [ + { + "group_name": "Disabled" + } + ], + "ChromeDashboard": [ + { + "group_name": "Default" + } + ], + "ExtensionContentVerification": [ + { + "group_name": "Enforce" + } + ], + "ExtensionInstallVerification": [ + { + "group_name": "Enforce" + } + ], + "GoogleNow": [ + { + "group_name": "Enable" + } + ], + "IconNTP": [ + { + "group_name": "Default" + } + ], + "NewProfileManagement": [ + { + "group_name": "Enabled" + } + ], + "PasswordGeneration": [ + { + "group_name": "Disabled" + } + ], + "RefreshTokenDeviceId": [ + { + "group_name": "Enabled" + } + ], + "SHA1IdentityUIWarning": [ + { + "group_name": "Enabled" + } + ], + "SHA1ToolbarUIJanuary2016": [ + { + "group_name": "Warning" + } + ], + "SHA1ToolbarUIJanuary2017": [ + { + "group_name": "Error" + } + ], + "SRTPromptFieldTrial": [ + { + "group_name": "Default" + } + ], + "SettingsEnforcement": [ + { + "group_name": "enforce_always_with_extensions_and_dse" + } + ], + "ShowAppLauncherPromo": [ + { + "group_name": "ShowPromoUntilDismissed" + } + ], + "UwSInterstitialStatus": [ + { + "group_name": "On" + } + ], + "VoiceTrigger": [ + { + "group_name": "Install" + } + ] +} diff --git a/engine/src/flutter/testing/xvfb.py b/engine/src/flutter/testing/xvfb.py new file mode 100755 index 0000000000..e7280f4d31 --- /dev/null +++ b/engine/src/flutter/testing/xvfb.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Runs the test with xvfb on linux. Runs the test normally on other platforms. + +For simplicity in gyp targets, this script just runs the test normal on +non-linux platforms. +""" + +import os +import platform +import signal +import subprocess +import sys + +import test_env + + +def kill(pid): + """Kills a process and traps exception if the process doesn't exist anymore. + """ + # If the process doesn't exist, it raises an exception that we can ignore. + try: + os.kill(pid, signal.SIGKILL) + except OSError: + pass + + +def get_xvfb_path(server_dir): + """Figures out which X server to use.""" + xvfb_path = os.path.join(server_dir, 'Xvfb.' + platform.architecture()[0]) + if not os.path.exists(xvfb_path): + xvfb_path = os.path.join(server_dir, 'Xvfb') + if not os.path.exists(xvfb_path): + print >> sys.stderr, ( + 'No Xvfb found in designated server path: %s' % server_dir) + raise Exception('No virtual server') + return xvfb_path + + +def start_xvfb(xvfb_path, display): + """Starts a virtual X server that we run the tests in. + + This makes it so we can run the tests even if we didn't start the tests from + an X session. + + Args: + xvfb_path: Path to Xvfb. + """ + cmd = [xvfb_path, display, '-screen', '0', '1024x768x24', '-ac', + '-nolisten', 'tcp', '-dpi', '96'] + try: + proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except OSError: + print >> sys.stderr, 'Failed to run %s' % ' '.join(cmd) + return + return proc + + +def wait_for_xvfb(xdisplaycheck, env): + """Waits for xvfb to be fully initialized by using xdisplaycheck.""" + try: + _logs = subprocess.check_output( + [xdisplaycheck], + stderr=subprocess.STDOUT, + env=env) + except OSError: + print >> sys.stderr, 'Failed to load %s with cwd=%s' % ( + xdisplaycheck, os.getcwd()) + return False + except subprocess.CalledProcessError as e: + print >> sys.stderr, ( + 'Xvfb failed to load properly (code %d) according to %s' % + (e.returncode, xdisplaycheck)) + return False + + return True + + +def run_executable(cmd, build_dir, env): + """Runs an executable within a xvfb buffer on linux or normally on other + platforms. + + Requires that both xvfb and openbox are installed on linux. + + Detects recursion with an environment variable and do not create a recursive X + buffer if present. + """ + # First look if we are inside a display. + if env.get('_CHROMIUM_INSIDE_XVFB') == '1': + # No need to recurse. + return test_env.run_executable(cmd, env) + + pid = None + xvfb = 'Xvfb' + try: + if sys.platform == 'linux2': + # Defaults to X display 9. + display = ':9' + xvfb_proc = start_xvfb(xvfb, display) + if not xvfb_proc or not xvfb_proc.pid: + return 1 + env['DISPLAY'] = display + if not wait_for_xvfb(os.path.join(build_dir, 'xdisplaycheck'), env): + rc = xvfb_proc.poll() + if rc is None: + print 'Xvfb still running, stopping.' + xvfb_proc.terminate() + else: + print 'Xvfb exited, code %d' % rc + + print 'Xvfb output:' + for l in xvfb_proc.communicate()[0].splitlines(): + print '> %s' % l + + return 3 + # Inhibit recursion. + env['_CHROMIUM_INSIDE_XVFB'] = '1' + # Some ChromeOS tests need a window manager. Technically, it could be + # another script but that would be overkill. + try: + wm_cmd = ['openbox'] + subprocess.Popen( + wm_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) + except OSError: + print >> sys.stderr, 'Failed to run %s' % ' '.join(wm_cmd) + return 1 + return test_env.run_executable(cmd, env) + finally: + if pid: + kill(pid) + + +def main(): + if len(sys.argv) < 3: + print >> sys.stderr, ( + 'Usage: xvfb.py [path to build_dir] [command args...]') + return 2 + return run_executable(sys.argv[2:], sys.argv[1], os.environ.copy()) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/engine/src/flutter/third_party/android_testrunner/LICENSE b/engine/src/flutter/third_party/android_testrunner/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/engine/src/flutter/third_party/android_testrunner/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/engine/src/flutter/third_party/android_testrunner/OWNERS b/engine/src/flutter/third_party/android_testrunner/OWNERS new file mode 100644 index 0000000000..69f908a316 --- /dev/null +++ b/engine/src/flutter/third_party/android_testrunner/OWNERS @@ -0,0 +1,3 @@ +jbudorick@chromium.org +klundberg@chromium.org +perezju@chromium.org diff --git a/engine/src/flutter/third_party/android_testrunner/README.chromium b/engine/src/flutter/third_party/android_testrunner/README.chromium new file mode 100644 index 0000000000..6ca8498f75 --- /dev/null +++ b/engine/src/flutter/third_party/android_testrunner/README.chromium @@ -0,0 +1,36 @@ +Name: Android Test runner script +URL: http://source.android.com +Version: 3.1.4 +License: Apache Version 2.0 +License File: NOT_SHIPPED +Security Critical: no + +Description: +This package is the scripts used to run the unit test for Android and from +Android Gingerbread. + +Local Modifications: +1. Added |silent_log| argument to |StartInstrumentation| so that output can be + suppressed. +2. Changed error message handling in |StartInstrumentation| from |shortMsg| to + |longMsg| to provide more information when debugging. +3. Applied the patch file patch.diff to fix a race condition and subproccess + bugs in run_command.py. +4. Fixed a bug where wait_time wasn't properly respected in + _WaitForShellCommandContents. +5. Added option to specify the ADB binary that should be used instead of always + using the ADB in the environment path. + +Here is the detail steps +1. Checkout Android source code + +$ repo init -u git://android.git.kernel.org/platform/manifest.git -b gingerbread +$ repo sync +$ cd development +$ git reset --hard 76f63551d36b1de63c63f357e5f0646ed8c306bb + +2. Copy the related files from + /development/testrunner/ + +More information can be found in +http://source.android.com/source/downloading.html diff --git a/engine/src/flutter/third_party/android_testrunner/adb_interface.py b/engine/src/flutter/third_party/android_testrunner/adb_interface.py new file mode 100644 index 0000000000..306f18690b --- /dev/null +++ b/engine/src/flutter/third_party/android_testrunner/adb_interface.py @@ -0,0 +1,522 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2008, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Provides an interface to communicate with the device via the adb command. + +Assumes adb binary is currently on system path. +""" +# Python imports +import os +import string +import time + +# local imports +import am_instrument_parser +import errors +import logger +import run_command + + +class AdbInterface: + """Helper class for communicating with Android device via adb.""" + + DEVICE_TRACE_DIR = "/data/test_results/" + + def __init__(self, adb_path='adb'): + """Constructor. + + Args: + adb_path: Absolute path to the adb binary that should be used. Defaults + to the adb in the environment path. + """ + self._adb_path = adb_path + # argument to pass to adb, to direct command to specific device + self._target_arg = "" + + def SetEmulatorTarget(self): + """Direct all future commands to the only running emulator.""" + self._target_arg = "-e" + + def SetDeviceTarget(self): + """Direct all future commands to the only connected USB device.""" + self._target_arg = "-d" + + def SetTargetSerial(self, serial): + """Direct all future commands to Android target with the given serial.""" + self._target_arg = "-s %s" % serial + + def SendCommand(self, command_string, timeout_time=20, retry_count=3): + """Send a command via adb. + + Args: + command_string: adb command to run + timeout_time: number of seconds to wait for command to respond before + retrying + retry_count: number of times to retry command before raising + WaitForResponseTimedOutError + Returns: + string output of command + + Raises: + WaitForResponseTimedOutError if device does not respond to command within time + """ + adb_cmd = "%s %s %s" % (self._adb_path, self._target_arg, command_string) + logger.SilentLog("about to run %s" % adb_cmd) + return run_command.RunCommand(adb_cmd, timeout_time=timeout_time, + retry_count=retry_count) + + def SendShellCommand(self, cmd, timeout_time=20, retry_count=3): + """Send a adb shell command. + + Args: + cmd: adb shell command to run + timeout_time: number of seconds to wait for command to respond before + retrying + retry_count: number of times to retry command before raising + WaitForResponseTimedOutError + + Returns: + string output of command + + Raises: + WaitForResponseTimedOutError: if device does not respond to command + """ + return self.SendCommand("shell %s" % cmd, timeout_time=timeout_time, + retry_count=retry_count) + + def BugReport(self, path): + """Dumps adb bugreport to the file specified by the path. + + Args: + path: Path of the file where adb bugreport is dumped to. + """ + bug_output = self.SendShellCommand("bugreport", timeout_time=60) + bugreport_file = open(path, "w") + bugreport_file.write(bug_output) + bugreport_file.close() + + def Push(self, src, dest): + """Pushes the file src onto the device at dest. + + Args: + src: file path of host file to push + dest: destination absolute file path on device + """ + self.SendCommand("push %s %s" % (src, dest), timeout_time=60) + + def Pull(self, src, dest): + """Pulls the file src on the device onto dest on the host. + + Args: + src: absolute file path of file on device to pull + dest: destination file path on host + + Returns: + True if success and False otherwise. + """ + # Create the base dir if it doesn't exist already + if not os.path.exists(os.path.dirname(dest)): + os.makedirs(os.path.dirname(dest)) + + if self.DoesFileExist(src): + self.SendCommand("pull %s %s" % (src, dest), timeout_time=60) + return True + else: + logger.Log("ADB Pull Failed: Source file %s does not exist." % src) + return False + + def DoesFileExist(self, src): + """Checks if the given path exists on device target. + + Args: + src: file path to be checked. + + Returns: + True if file exists + """ + + output = self.SendShellCommand("ls %s" % src) + error = "No such file or directory" + + if error in output: + return False + return True + + def EnableAdbRoot(self): + """Enable adb root on device.""" + output = self.SendCommand("root") + if "adbd is already running as root" in output: + return True + elif "restarting adbd as root" in output: + # device will disappear from adb, wait for it to come back + self.SendCommand("wait-for-device") + return True + else: + logger.Log("Unrecognized output from adb root: %s" % output) + return False + + def StartInstrumentationForPackage( + self, package_name, runner_name, timeout_time=60*10, + no_window_animation=False, instrumentation_args={}): + """Run instrumentation test for given package and runner. + + Equivalent to StartInstrumentation, except instrumentation path is + separated into its package and runner components. + """ + instrumentation_path = "%s/%s" % (package_name, runner_name) + return self.StartInstrumentation(instrumentation_path, timeout_time=timeout_time, + no_window_animation=no_window_animation, + instrumentation_args=instrumentation_args) + + def StartInstrumentation( + self, instrumentation_path, timeout_time=60*10, no_window_animation=False, + profile=False, instrumentation_args={}, silent_log=False): + + """Runs an instrumentation class on the target. + + Returns a dictionary containing the key value pairs from the + instrumentations result bundle and a list of TestResults. Also handles the + interpreting of error output from the device and raises the necessary + exceptions. + + Args: + instrumentation_path: string. It should be the fully classified package + name, and instrumentation test runner, separated by "/" + e.g. com.android.globaltimelaunch/.GlobalTimeLaunch + timeout_time: Timeout value for the am command. + no_window_animation: boolean, Whether you want window animations enabled + or disabled + profile: If True, profiling will be turned on for the instrumentation. + instrumentation_args: Dictionary of key value bundle arguments to pass to + instrumentation. + silent_log: If True, the invocation of the instrumentation test runner + will not be logged. + + Returns: + (test_results, inst_finished_bundle) + + test_results: a list of TestResults + inst_finished_bundle (dict): Key/value pairs contained in the bundle that + is passed into ActivityManager.finishInstrumentation(). Included in this + bundle is the return code of the Instrumentation process, any error + codes reported by the activity manager, and any results explicitly added + by the instrumentation code. + + Raises: + WaitForResponseTimedOutError: if timeout occurred while waiting for + response to adb instrument command + DeviceUnresponsiveError: if device system process is not responding + InstrumentationError: if instrumentation failed to run + """ + + command_string = self._BuildInstrumentationCommandPath( + instrumentation_path, no_window_animation=no_window_animation, + profile=profile, raw_mode=True, + instrumentation_args=instrumentation_args) + if silent_log: + logger.SilentLog(command_string) + else: + logger.Log(command_string) + (test_results, inst_finished_bundle) = ( + am_instrument_parser.ParseAmInstrumentOutput( + self.SendShellCommand(command_string, timeout_time=timeout_time, + retry_count=2))) + if "code" not in inst_finished_bundle: + logger.Log('No code available. inst_finished_bundle contains: %s ' + % inst_finished_bundle) + raise errors.InstrumentationError("no test results... device setup " + "correctly?") + + if inst_finished_bundle["code"] == "0": + long_msg_result = "no error message" + if "longMsg" in inst_finished_bundle: + long_msg_result = inst_finished_bundle["longMsg"] + logger.Log("Error! Test run failed: %s" % long_msg_result) + raise errors.InstrumentationError(long_msg_result) + + if "INSTRUMENTATION_ABORTED" in inst_finished_bundle: + logger.Log("INSTRUMENTATION ABORTED!") + raise errors.DeviceUnresponsiveError + + return (test_results, inst_finished_bundle) + + def StartInstrumentationNoResults( + self, package_name, runner_name, no_window_animation=False, + raw_mode=False, instrumentation_args={}): + """Runs instrumentation and dumps output to stdout. + + Equivalent to StartInstrumentation, but will dump instrumentation + 'normal' output to stdout, instead of parsing return results. Command will + never timeout. + """ + adb_command_string = self.PreviewInstrumentationCommand( + package_name, runner_name, no_window_animation=no_window_animation, + raw_mode=raw_mode, instrumentation_args=instrumentation_args) + logger.Log(adb_command_string) + run_command.RunCommand(adb_command_string, return_output=False) + + def PreviewInstrumentationCommand( + self, package_name, runner_name, no_window_animation=False, + raw_mode=False, instrumentation_args={}): + """Returns a string of adb command that will be executed.""" + inst_command_string = self._BuildInstrumentationCommand( + package_name, runner_name, no_window_animation=no_window_animation, + raw_mode=raw_mode, instrumentation_args=instrumentation_args) + command_string = "adb %s shell %s" % (self._target_arg, inst_command_string) + return command_string + + def _BuildInstrumentationCommand( + self, package, runner_name, no_window_animation=False, profile=False, + raw_mode=True, instrumentation_args={}): + instrumentation_path = "%s/%s" % (package, runner_name) + + return self._BuildInstrumentationCommandPath( + instrumentation_path, no_window_animation=no_window_animation, + profile=profile, raw_mode=raw_mode, + instrumentation_args=instrumentation_args) + + def _BuildInstrumentationCommandPath( + self, instrumentation_path, no_window_animation=False, profile=False, + raw_mode=True, instrumentation_args={}): + command_string = "am instrument" + if no_window_animation: + command_string += " --no_window_animation" + if profile: + self._CreateTraceDir() + command_string += ( + " -p %s/%s.dmtrace" % + (self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1])) + + for key, value in instrumentation_args.items(): + command_string += " -e %s '%s'" % (key, value) + if raw_mode: + command_string += " -r" + command_string += " -w %s" % instrumentation_path + return command_string + + def _CreateTraceDir(self): + ls_response = self.SendShellCommand("ls /data/trace") + if ls_response.strip("#").strip(string.whitespace) != "": + self.SendShellCommand("create /data/trace", "mkdir /data/trace") + self.SendShellCommand("make /data/trace world writeable", + "chmod 777 /data/trace") + + def WaitForDevicePm(self, wait_time=120): + """Waits for targeted device's package manager to be up. + + Args: + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and pm still does not + respond. + """ + logger.Log("Waiting for device package manager...") + self.SendCommand("wait-for-device", timeout_time=wait_time, retry_count=0) + # Now the device is there, but may not be running. + # Query the package manager with a basic command + try: + self._WaitForShellCommandContents("pm path android", "package:", + wait_time) + except errors.WaitForResponseTimedOutError: + raise errors.WaitForResponseTimedOutError( + "Package manager did not respond after %s seconds" % wait_time) + + def WaitForInstrumentation(self, package_name, runner_name, wait_time=120): + """Waits for given instrumentation to be present on device + + Args: + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and instrumentation + still not present. + """ + instrumentation_path = "%s/%s" % (package_name, runner_name) + logger.Log("Waiting for instrumentation to be present") + # Query the package manager + try: + command = "pm list instrumentation | grep %s" % instrumentation_path + self._WaitForShellCommandContents(command, "instrumentation:", wait_time, + raise_abort=False) + except errors.WaitForResponseTimedOutError : + logger.Log( + "Could not find instrumentation %s on device. Does the " + "instrumentation in test's AndroidManifest.xml match definition" + "in test_defs.xml?" % instrumentation_path) + raise + + def WaitForProcess(self, name, wait_time=120): + """Wait until a process is running on the device. + + Args: + name: the process name as it appears in `ps` + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and the process is + still not running + """ + logger.Log("Waiting for process %s" % name) + self.SendCommand("wait-for-device") + self._WaitForShellCommandContents("ps", name, wait_time) + + def WaitForProcessEnd(self, name, wait_time=120): + """Wait until a process is no longer running on the device. + + Args: + name: the process name as it appears in `ps` + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and the process is + still running + """ + logger.Log("Waiting for process %s to end" % name) + self._WaitForShellCommandContents("ps", name, wait_time, invert=True) + + def _WaitForShellCommandContents(self, command, expected, wait_time, + raise_abort=True, invert=False): + """Wait until the response to a command contains a given output. + + Assumes that a only successful execution of "adb shell " contains + the substring expected. Assumes that a device is present. + + Args: + command: adb shell command to execute + expected: the string that should appear to consider the + command successful. + wait_time: time in seconds to wait + raise_abort: if False, retry when executing the command raises an + AbortError, rather than failing. + invert: if True, wait until the command output no longer contains the + expected contents. + + Raises: + WaitForResponseTimedOutError: If wait_time elapses and the command has not + returned an output containing expected yet. + """ + # Query the device with the command + success = False + attempts = 0 + wait_period = 5 + while not success and (attempts*wait_period) < wait_time: + # assume the command will always contain expected in the success case + try: + output = self.SendShellCommand(command, retry_count=1, + timeout_time=wait_time) + if ((not invert and expected in output) + or (invert and expected not in output)): + success = True + except errors.AbortError, e: + if raise_abort: + raise + # ignore otherwise + + if not success: + time.sleep(wait_period) + attempts += 1 + + if not success: + raise errors.WaitForResponseTimedOutError() + + def WaitForBootComplete(self, wait_time=120): + """Waits for targeted device's bootcomplete flag to be set. + + Args: + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and pm still does not + respond. + """ + logger.Log("Waiting for boot complete...") + self.SendCommand("wait-for-device") + # Now the device is there, but may not be running. + # Query the package manager with a basic command + boot_complete = False + attempts = 0 + wait_period = 5 + while not boot_complete and (attempts*wait_period) < wait_time: + output = self.SendShellCommand("getprop dev.bootcomplete", retry_count=1) + output = output.strip() + if output == "1": + boot_complete = True + else: + time.sleep(wait_period) + attempts += 1 + if not boot_complete: + raise errors.WaitForResponseTimedOutError( + "dev.bootcomplete flag was not set after %s seconds" % wait_time) + + def Sync(self, retry_count=3, runtime_restart=False): + """Perform a adb sync. + + Blocks until device package manager is responding. + + Args: + retry_count: number of times to retry sync before failing + runtime_restart: stop runtime during sync and restart afterwards, useful + for syncing system libraries (core, framework etc) + + Raises: + WaitForResponseTimedOutError if package manager does not respond + AbortError if unrecoverable error occurred + """ + output = "" + error = None + if runtime_restart: + self.SendShellCommand("setprop ro.monkey 1", retry_count=retry_count) + # manual rest bootcomplete flag + self.SendShellCommand("setprop dev.bootcomplete 0", + retry_count=retry_count) + self.SendShellCommand("stop", retry_count=retry_count) + + try: + output = self.SendCommand("sync", retry_count=retry_count) + except errors.AbortError, e: + error = e + output = e.msg + if "Read-only file system" in output: + logger.SilentLog(output) + logger.Log("Remounting read-only filesystem") + self.SendCommand("remount") + output = self.SendCommand("sync", retry_count=retry_count) + elif "No space left on device" in output: + logger.SilentLog(output) + logger.Log("Restarting device runtime") + self.SendShellCommand("stop", retry_count=retry_count) + output = self.SendCommand("sync", retry_count=retry_count) + self.SendShellCommand("start", retry_count=retry_count) + elif error is not None: + # exception occurred that cannot be recovered from + raise error + logger.SilentLog(output) + if runtime_restart: + # start runtime and wait till boot complete flag is set + self.SendShellCommand("start", retry_count=retry_count) + self.WaitForBootComplete() + # press the MENU key, this will disable key guard if runtime is started + # with ro.monkey set to 1 + self.SendShellCommand("input keyevent 82", retry_count=retry_count) + else: + self.WaitForDevicePm() + return output + + def GetSerialNumber(self): + """Returns the serial number of the targeted device.""" + return self.SendCommand("get-serialno").strip() diff --git a/engine/src/flutter/third_party/android_testrunner/am_instrument_parser.py b/engine/src/flutter/third_party/android_testrunner/am_instrument_parser.py new file mode 100644 index 0000000000..4554c4d565 --- /dev/null +++ b/engine/src/flutter/third_party/android_testrunner/am_instrument_parser.py @@ -0,0 +1,169 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2008, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Module that assists in parsing the output of "am instrument" commands run on +the device.""" + +import re +import string + + +def ParseAmInstrumentOutput(result): + """Given the raw output of an "am instrument" command that targets and + InstrumentationTestRunner, return structured data. + + Args: + result (string): Raw output of "am instrument" + + Return + (test_results, inst_finished_bundle) + + test_results (list of am_output_parser.TestResult) + inst_finished_bundle (dict): Key/value pairs contained in the bundle that is + passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return + code of the Instrumentation process, any error codes reported by the + activity manager, and any results explicity added by the instrumentation + code. + """ + + re_status_code = re.compile(r'INSTRUMENTATION_STATUS_CODE: (?P-?\d)$') + test_results = [] + inst_finished_bundle = {} + + result_block_string = "" + for line in result.splitlines(): + result_block_string += line + '\n' + + if "INSTRUMENTATION_STATUS_CODE:" in line: + test_result = TestResult(result_block_string) + if test_result.GetStatusCode() == 1: # The test started + pass + elif test_result.GetStatusCode() in [0, -1, -2]: + test_results.append(test_result) + else: + pass + result_block_string = "" + if "INSTRUMENTATION_CODE:" in line: + inst_finished_bundle = _ParseInstrumentationFinishedBundle(result_block_string) + result_block_string = "" + + return (test_results, inst_finished_bundle) + + +def _ParseInstrumentationFinishedBundle(result): + """Given the raw output of "am instrument" returns a dictionary of the + key/value pairs from the bundle passed into + ActivityManager.finishInstrumentation(). + + Args: + result (string): Raw output of "am instrument" + + Return: + inst_finished_bundle (dict): Key/value pairs contained in the bundle that is + passed into ActivityManager.finishInstrumentation(). Included in this bundle is the return + code of the Instrumentation process, any error codes reported by the + activity manager, and any results explicity added by the instrumentation + code. + """ + + re_result = re.compile(r'INSTRUMENTATION_RESULT: ([^=]+)=(.*)$') + re_code = re.compile(r'INSTRUMENTATION_CODE: (\-?\d)$') + result_dict = {} + key = '' + val = '' + last_tag = '' + + for line in result.split('\n'): + line = line.strip(string.whitespace) + if re_result.match(line): + last_tag = 'INSTRUMENTATION_RESULT' + key = re_result.search(line).group(1).strip(string.whitespace) + if key.startswith('performance.'): + key = key[len('performance.'):] + val = re_result.search(line).group(2).strip(string.whitespace) + try: + result_dict[key] = float(val) + except ValueError: + result_dict[key] = val + except TypeError: + result_dict[key] = val + elif re_code.match(line): + last_tag = 'INSTRUMENTATION_CODE' + key = 'code' + val = re_code.search(line).group(1).strip(string.whitespace) + result_dict[key] = val + elif 'INSTRUMENTATION_ABORTED:' in line: + last_tag = 'INSTRUMENTATION_ABORTED' + key = 'INSTRUMENTATION_ABORTED' + val = '' + result_dict[key] = val + elif last_tag == 'INSTRUMENTATION_RESULT': + result_dict[key] += '\n' + line + + if not result_dict.has_key('code'): + result_dict['code'] = '0' + result_dict['shortMsg'] = "No result returned from instrumentation" + + return result_dict + + +class TestResult(object): + """A class that contains information about a single test result.""" + + def __init__(self, result_block_string): + """ + Args: + result_block_string (string): Is a single "block" of output. A single + "block" would be either a "test started" status report, or a "test + finished" status report. + """ + + self._test_name = None + self._status_code = None + self._failure_reason = None + self._fields_map = {} + + re_status_code = re.search(r'INSTRUMENTATION_STATUS_CODE: ' + '(?P1|0|-1|-2)', result_block_string) + re_fields = re.compile(r'INSTRUMENTATION_STATUS: ' + '(?P[\w.]+)=(?P.*?)(?=\nINSTRUMENTATION_STATUS)', re.DOTALL) + + for field in re_fields.finditer(result_block_string): + key, value = (field.group('key').strip(), field.group('value').strip()) + if key.startswith('performance.'): + key = key[len('performance.'):] + self._fields_map[key] = value + self._fields_map.setdefault('class') + self._fields_map.setdefault('test') + + self._test_name = '%s:%s' % (self._fields_map['class'], + self._fields_map['test']) + self._status_code = int(re_status_code.group('status_code')) + if 'stack' in self._fields_map: + self._failure_reason = self._fields_map['stack'] + + def GetTestName(self): + return self._test_name + + def GetStatusCode(self): + return self._status_code + + def GetFailureReason(self): + return self._failure_reason + + def GetResultFields(self): + return self._fields_map diff --git a/engine/src/flutter/third_party/android_testrunner/errors.py b/engine/src/flutter/third_party/android_testrunner/errors.py new file mode 100644 index 0000000000..e163dd45b3 --- /dev/null +++ b/engine/src/flutter/third_party/android_testrunner/errors.py @@ -0,0 +1,46 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2008, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Defines common exception classes for this package.""" + + +class MsgException(Exception): + """Generic exception with an optional string msg.""" + def __init__(self, msg=""): + self.msg = msg + + +class WaitForResponseTimedOutError(Exception): + """We sent a command and had to wait too long for response.""" + + +class DeviceUnresponsiveError(Exception): + """Device is unresponsive to command.""" + + +class InstrumentationError(Exception): + """Failed to run instrumentation.""" + + +class AbortError(MsgException): + """Generic exception that indicates a fatal error has occurred and program + execution should be aborted.""" + + +class ParseError(MsgException): + """Raised when xml data to parse has unrecognized format.""" + diff --git a/engine/src/flutter/third_party/android_testrunner/logger.py b/engine/src/flutter/third_party/android_testrunner/logger.py new file mode 100644 index 0000000000..61463a198c --- /dev/null +++ b/engine/src/flutter/third_party/android_testrunner/logger.py @@ -0,0 +1,96 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2007, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Simple logging utility. Dumps log messages to stdout, and optionally, to a +log file. + +Init(path) must be called to enable logging to a file +""" + +import datetime + +_LOG_FILE = None +_verbose = False +_log_time = True + +def Init(log_file_path): + """Set the path to the log file""" + global _LOG_FILE + _LOG_FILE = log_file_path + print "Using log file: %s" % _LOG_FILE + +def GetLogFilePath(): + """Returns the path and name of the Log file""" + global _LOG_FILE + return _LOG_FILE + +def Log(new_str): + """Appends new_str to the end of _LOG_FILE and prints it to stdout. + + Args: + # new_str is a string. + new_str: 'some message to log' + """ + msg = _PrependTimeStamp(new_str) + print msg + _WriteLog(msg) + +def _WriteLog(msg): + global _LOG_FILE + if _LOG_FILE is not None: + file_handle = file(_LOG_FILE, 'a') + file_handle.write('\n' + str(msg)) + file_handle.close() + +def _PrependTimeStamp(log_string): + """Returns the log_string prepended with current timestamp """ + global _log_time + if _log_time: + return "# %s: %s" % (datetime.datetime.now().strftime("%m/%d/%y %H:%M:%S"), + log_string) + else: + # timestamp logging disabled + return log_string + +def SilentLog(new_str): + """Silently log new_str. Unless verbose mode is enabled, will log new_str + only to the log file + Args: + # new_str is a string. + new_str: 'some message to log' + """ + global _verbose + msg = _PrependTimeStamp(new_str) + if _verbose: + print msg + _WriteLog(msg) + +def SetVerbose(new_verbose=True): + """ Enable or disable verbose logging""" + global _verbose + _verbose = new_verbose + +def SetTimestampLogging(new_timestamp=True): + """ Enable or disable outputting a timestamp with each log entry""" + global _log_time + _log_time = new_timestamp + +def main(): + pass + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/third_party/android_testrunner/patch.diff b/engine/src/flutter/third_party/android_testrunner/patch.diff new file mode 100644 index 0000000000..9b067cd50b --- /dev/null +++ b/engine/src/flutter/third_party/android_testrunner/patch.diff @@ -0,0 +1,104 @@ +diff --git a/third_party/android_testrunner/run_command.py b/third_party/android_testrunner/run_command.py +index d398daa..6b84156 100644 +--- a/third_party/android_testrunner/run_command.py ++++ b/third_party/android_testrunner/run_command.py +@@ -19,6 +19,7 @@ + import os + import signal + import subprocess ++import tempfile + import threading + import time + +@@ -80,31 +81,36 @@ def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None): + """ + start_time = time.time() + so = [] +- pid = [] + global _abort_on_error, error_occurred + error_occurred = False + ++ if return_output: ++ output_dest = tempfile.TemporaryFile(bufsize=0) ++ else: ++ # None means direct to stdout ++ output_dest = None ++ if stdin_input: ++ stdin_dest = subprocess.PIPE ++ else: ++ stdin_dest = None ++ pipe = subprocess.Popen( ++ cmd, ++ executable='/bin/bash', ++ stdin=stdin_dest, ++ stdout=output_dest, ++ stderr=subprocess.STDOUT, ++ shell=True, close_fds=True, ++ preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)) ++ + def Run(): + global error_occurred +- if return_output: +- output_dest = subprocess.PIPE +- else: +- # None means direct to stdout +- output_dest = None +- if stdin_input: +- stdin_dest = subprocess.PIPE +- else: +- stdin_dest = None +- pipe = subprocess.Popen( +- cmd, +- executable='/bin/bash', +- stdin=stdin_dest, +- stdout=output_dest, +- stderr=subprocess.STDOUT, +- shell=True) +- pid.append(pipe.pid) + try: +- output = pipe.communicate(input=stdin_input)[0] ++ pipe.communicate(input=stdin_input) ++ output = None ++ if return_output: ++ output_dest.seek(0) ++ output = output_dest.read() ++ output_dest.close() + if output is not None and len(output) > 0: + so.append(output) + except OSError, e: +@@ -119,27 +125,17 @@ def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None): + + t = threading.Thread(target=Run) + t.start() +- +- break_loop = False +- while not break_loop: +- if not t.isAlive(): +- break_loop = True +- +- # Check the timeout +- if (not break_loop and timeout_time is not None +- and time.time() > start_time + timeout_time): +- try: +- os.kill(pid[0], signal.SIGKILL) +- except OSError: +- # process already dead. No action required. +- pass +- ++ t.join(timeout_time) ++ if t.isAlive(): ++ try: ++ pipe.kill() ++ except OSError: ++ # Can't kill a dead process. ++ pass ++ finally: + logger.SilentLog("about to raise a timeout for: %s" % cmd) + raise errors.WaitForResponseTimedOutError +- if not break_loop: +- time.sleep(0.1) + +- t.join() + output = "".join(so) + if _abort_on_error and error_occurred: + raise errors.AbortError(msg=output) diff --git a/engine/src/flutter/third_party/android_testrunner/run_command.py b/engine/src/flutter/third_party/android_testrunner/run_command.py new file mode 100644 index 0000000000..6b84156fe0 --- /dev/null +++ b/engine/src/flutter/third_party/android_testrunner/run_command.py @@ -0,0 +1,192 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2007, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# System imports +import os +import signal +import subprocess +import tempfile +import threading +import time + +# local imports +import errors +import logger + +_abort_on_error = False + +def SetAbortOnError(abort=True): + """Sets behavior of RunCommand to throw AbortError if command process returns + a negative error code""" + global _abort_on_error + _abort_on_error = abort + +def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True, + stdin_input=None): + """Spawn and retry a subprocess to run the given shell command. + + Args: + cmd: shell command to run + timeout_time: time in seconds to wait for command to run before aborting. + retry_count: number of times to retry command + return_output: if True return output of command as string. Otherwise, + direct output of command to stdout. + stdin_input: data to feed to stdin + Returns: + output of command + """ + result = None + while True: + try: + result = RunOnce(cmd, timeout_time=timeout_time, + return_output=return_output, stdin_input=stdin_input) + except errors.WaitForResponseTimedOutError: + if retry_count == 0: + raise + retry_count -= 1 + logger.Log("No response for %s, retrying" % cmd) + else: + # Success + return result + +def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None): + """Spawns a subprocess to run the given shell command. + + Args: + cmd: shell command to run + timeout_time: time in seconds to wait for command to run before aborting. + return_output: if True return output of command as string. Otherwise, + direct output of command to stdout. + stdin_input: data to feed to stdin + Returns: + output of command + Raises: + errors.WaitForResponseTimedOutError if command did not complete within + timeout_time seconds. + errors.AbortError is command returned error code and SetAbortOnError is on. + """ + start_time = time.time() + so = [] + global _abort_on_error, error_occurred + error_occurred = False + + if return_output: + output_dest = tempfile.TemporaryFile(bufsize=0) + else: + # None means direct to stdout + output_dest = None + if stdin_input: + stdin_dest = subprocess.PIPE + else: + stdin_dest = None + pipe = subprocess.Popen( + cmd, + executable='/bin/bash', + stdin=stdin_dest, + stdout=output_dest, + stderr=subprocess.STDOUT, + shell=True, close_fds=True, + preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)) + + def Run(): + global error_occurred + try: + pipe.communicate(input=stdin_input) + output = None + if return_output: + output_dest.seek(0) + output = output_dest.read() + output_dest.close() + if output is not None and len(output) > 0: + so.append(output) + except OSError, e: + logger.SilentLog("failed to retrieve stdout from: %s" % cmd) + logger.Log(e) + so.append("ERROR") + error_occurred = True + if pipe.returncode: + logger.SilentLog("Error: %s returned %d error code" %(cmd, + pipe.returncode)) + error_occurred = True + + t = threading.Thread(target=Run) + t.start() + t.join(timeout_time) + if t.isAlive(): + try: + pipe.kill() + except OSError: + # Can't kill a dead process. + pass + finally: + logger.SilentLog("about to raise a timeout for: %s" % cmd) + raise errors.WaitForResponseTimedOutError + + output = "".join(so) + if _abort_on_error and error_occurred: + raise errors.AbortError(msg=output) + + return "".join(so) + + +def RunHostCommand(binary, valgrind=False): + """Run a command on the host (opt using valgrind). + + Runs the host binary and returns the exit code. + If successfull, the output (stdout and stderr) are discarded, + but printed in case of error. + The command can be run under valgrind in which case all the + output are always discarded. + + Args: + binary: full path of the file to be run. + valgrind: If True the command will be run under valgrind. + + Returns: + The command exit code (int) + """ + if not valgrind: + subproc = subprocess.Popen(binary, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + subproc.wait() + if subproc.returncode != 0: # In case of error print the output + print subproc.communicate()[0] + return subproc.returncode + else: + # Need the full path to valgrind to avoid other versions on the system. + subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck", + "--leak-check=yes", "-q", binary], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + # Cannot rely on the retcode of valgrind. Instead look for an empty output. + valgrind_out = subproc.communicate()[0].strip() + if valgrind_out: + print valgrind_out + return 1 + else: + return 0 + + +def HasValgrind(): + """Check that /usr/bin/valgrind exists. + + We look for the fullpath to avoid picking up 'alternative' valgrind + on the system. + + Returns: + True if a system valgrind was found. + """ + return os.path.exists("/usr/bin/valgrind") diff --git a/engine/src/flutter/third_party/ashmem/BUILD.gn b/engine/src/flutter/third_party/ashmem/BUILD.gn new file mode 100644 index 0000000000..5e92b3773f --- /dev/null +++ b/engine/src/flutter/third_party/ashmem/BUILD.gn @@ -0,0 +1,12 @@ +# Copyright 2014 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. + +assert(is_android) + +source_set("ashmem") { + sources = [ + "ashmem-dev.c", + "ashmem.h", + ] +} diff --git a/engine/src/flutter/third_party/ashmem/LICENSE b/engine/src/flutter/third_party/ashmem/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/engine/src/flutter/third_party/ashmem/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/engine/src/flutter/third_party/ashmem/OWNERS b/engine/src/flutter/third_party/ashmem/OWNERS new file mode 100644 index 0000000000..938d2b5c48 --- /dev/null +++ b/engine/src/flutter/third_party/ashmem/OWNERS @@ -0,0 +1 @@ +digit@chromium.org diff --git a/engine/src/flutter/third_party/ashmem/README.chromium b/engine/src/flutter/third_party/ashmem/README.chromium new file mode 100644 index 0000000000..833e1f5b01 --- /dev/null +++ b/engine/src/flutter/third_party/ashmem/README.chromium @@ -0,0 +1,6 @@ +Name: Android +URL: http://source.android.com +Description: Android shared memory implementation. Only applies to OS_ANDROID. +Version: 7203eb2a8a29a7b721a48cd291700f38f3da1456 +Security Critical: yes +License: Apache 2.0 diff --git a/engine/src/flutter/third_party/ashmem/ashmem-dev.c b/engine/src/flutter/third_party/ashmem/ashmem-dev.c new file mode 100644 index 0000000000..2303369d81 --- /dev/null +++ b/engine/src/flutter/third_party/ashmem/ashmem-dev.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Implementation of the user-space ashmem API for devices, which have our + * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version, + * used by the simulator. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "ashmem.h" + +#define ASHMEM_DEVICE "/dev/ashmem" + +/* + * ashmem_create_region - creates a new ashmem region and returns the file + * descriptor, or <0 on error + * + * `name' is an optional label to give the region (visible in /proc/pid/maps) + * `size' is the size of the region, in page-aligned bytes + */ +int ashmem_create_region(const char *name, size_t size) +{ + int fd, ret; + + fd = open(ASHMEM_DEVICE, O_RDWR); + if (fd < 0) + return fd; + + if (name) { + char buf[ASHMEM_NAME_LEN]; + + strlcpy(buf, name, sizeof(buf)); + ret = ioctl(fd, ASHMEM_SET_NAME, buf); + if (ret < 0) + goto error; + } + + ret = ioctl(fd, ASHMEM_SET_SIZE, size); + if (ret < 0) + goto error; + + return fd; + +error: + close(fd); + return ret; +} + +int ashmem_set_prot_region(int fd, int prot) +{ + return ioctl(fd, ASHMEM_SET_PROT_MASK, prot); +} + +int ashmem_pin_region(int fd, size_t offset, size_t len) +{ + struct ashmem_pin pin = { offset, len }; + return ioctl(fd, ASHMEM_PIN, &pin); +} + +int ashmem_unpin_region(int fd, size_t offset, size_t len) +{ + struct ashmem_pin pin = { offset, len }; + return ioctl(fd, ASHMEM_UNPIN, &pin); +} + +int ashmem_get_size_region(int fd) +{ + return ioctl(fd, ASHMEM_GET_SIZE, NULL); +} + +int ashmem_purge_all(void) +{ + const int fd = open(ASHMEM_DEVICE, O_RDWR); + if (fd < 0) + return fd; + const int ret = ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0); + close(fd); + return ret; +} diff --git a/engine/src/flutter/third_party/ashmem/ashmem.gyp b/engine/src/flutter/third_party/ashmem/ashmem.gyp new file mode 100644 index 0000000000..e26ac18f58 --- /dev/null +++ b/engine/src/flutter/third_party/ashmem/ashmem.gyp @@ -0,0 +1,16 @@ +# Copyright (c) 2011 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. + +{ + 'targets': [ + { + 'target_name': 'ashmem', + 'type': 'static_library', + 'sources': [ + 'ashmem.h', + 'ashmem-dev.c' + ], + }, + ], +} diff --git a/engine/src/flutter/third_party/ashmem/ashmem.h b/engine/src/flutter/third_party/ashmem/ashmem.h new file mode 100644 index 0000000000..7d411cc064 --- /dev/null +++ b/engine/src/flutter/third_party/ashmem/ashmem.h @@ -0,0 +1,46 @@ +/* third_party/ashmem/ashmem.h + ** + ** Copyright 2008 The Android Open Source Project + ** + ** This file is dual licensed. It may be redistributed and/or modified + ** under the terms of the Apache 2.0 License OR version 2 of the GNU + ** General Public License. + */ + +#ifndef _THIRD_PARTY_ASHMEM_H +#define _THIRD_PARTY_ASHMEM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int ashmem_create_region(const char *name, size_t size); +int ashmem_set_prot_region(int fd, int prot); +int ashmem_pin_region(int fd, size_t offset, size_t len); +int ashmem_unpin_region(int fd, size_t offset, size_t len); +int ashmem_get_size_region(int fd); +int ashmem_purge_all(void); + +#ifdef __cplusplus +} +#endif + +#ifndef __ASHMEMIOC /* in case someone included too */ + +#define ASHMEM_NAME_LEN 256 + +#define ASHMEM_NAME_DEF "dev/ashmem" + +/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */ +#define ASHMEM_NOT_PURGED 0 +#define ASHMEM_WAS_PURGED 1 + +/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */ +#define ASHMEM_IS_UNPINNED 0 +#define ASHMEM_IS_PINNED 1 + +#endif /* ! __ASHMEMIOC */ + +#endif /* _THIRD_PARTY_ASHMEM_H */ diff --git a/engine/src/flutter/third_party/binutils/.gitignore b/engine/src/flutter/third_party/binutils/.gitignore new file mode 100644 index 0000000000..5605b2f711 --- /dev/null +++ b/engine/src/flutter/third_party/binutils/.gitignore @@ -0,0 +1,8 @@ +binutils-* +*-chroot-* +output-* +Linux_ia32/*stamp* +Linux_ia32/*tar.bz2 +Linux_x64/*stamp* +Linux_x64/*tar.bz2 +*/Release diff --git a/engine/src/flutter/third_party/binutils/LICENSE b/engine/src/flutter/third_party/binutils/LICENSE new file mode 100644 index 0000000000..4a1bd6f39f --- /dev/null +++ b/engine/src/flutter/third_party/binutils/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/engine/src/flutter/third_party/binutils/Linux_ia32/binutils.tar.bz2.sha1 b/engine/src/flutter/third_party/binutils/Linux_ia32/binutils.tar.bz2.sha1 new file mode 100644 index 0000000000..94c02d294a --- /dev/null +++ b/engine/src/flutter/third_party/binutils/Linux_ia32/binutils.tar.bz2.sha1 @@ -0,0 +1 @@ +e73c227c9cfacfc5fecaa22ee3555b90925f96c2 \ No newline at end of file diff --git a/engine/src/flutter/third_party/binutils/Linux_x64/binutils.tar.bz2.sha1 b/engine/src/flutter/third_party/binutils/Linux_x64/binutils.tar.bz2.sha1 new file mode 100644 index 0000000000..b415234821 --- /dev/null +++ b/engine/src/flutter/third_party/binutils/Linux_x64/binutils.tar.bz2.sha1 @@ -0,0 +1 @@ +05497e34b29c01dd82df76d2fbdf017d4a2c4214 \ No newline at end of file diff --git a/engine/src/flutter/third_party/binutils/OWNERS b/engine/src/flutter/third_party/binutils/OWNERS new file mode 100644 index 0000000000..41edd86745 --- /dev/null +++ b/engine/src/flutter/third_party/binutils/OWNERS @@ -0,0 +1,3 @@ +thakis@chromium.org +thestig@chromium.org +mithro@mithis.com diff --git a/engine/src/flutter/third_party/binutils/README.chromium b/engine/src/flutter/third_party/binutils/README.chromium new file mode 100644 index 0000000000..d20934b0a1 --- /dev/null +++ b/engine/src/flutter/third_party/binutils/README.chromium @@ -0,0 +1,29 @@ +Name: binutils +URL: http://www.gnu.org/software/binutils/ +Version: 2.24 +License: GPL v2 +License File: NOT_SHIPPED +Security Critical: no + +Description: +This directory contains i386 and amd64 binaries of the binutils tools +(including gold linker). + +They were built from binutils-2.24 using the "build-all.sh" script on a Ubuntu +Precise. + +The script creates chroots for 32bit and 64bit Ubuntu Lucid and then builds +binutils inside the roots. + +Local patches: + * ehframe-race.patch for http://crbug.com/161942 from upstream change + https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit;h=635aa30e3ae9735e362cfd1cda2be9f7b65b32a2 + + * unlock-thin.patch for http://crbug.com/453195 from upstream change + https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit;h=2cfbf2fece582c29df348104b28677c38a8301f4 + + * plugin-dso-fix.patch for http://crbug.com/453195 from upstream change + https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commit;h=3c537f7fdb11f02f7082749f3f21dfdd2c2025e8 + + * (build-all.sh|build-one.sh|upload.sh) scripts for building the binutils + binaries and uploading them to Google storage. diff --git a/engine/src/flutter/third_party/binutils/build-all.sh b/engine/src/flutter/third_party/binutils/build-all.sh new file mode 100755 index 0000000000..a39984ca2b --- /dev/null +++ b/engine/src/flutter/third_party/binutils/build-all.sh @@ -0,0 +1,123 @@ +#!/bin/sh +# Copyright 2014 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. + +# Script to build binutils for both i386 and AMD64 Linux architectures. +# Must be run on an AMD64 supporting machine which has debootstrap and sudo +# installed. +# Uses Ubuntu Lucid chroots as build environment. + +set -e + +if [ x"$(whoami)" = x"root" ]; then + echo "Script must not be run as root." + exit 1 +fi +sudo -v + +OUTPUTDIR="${1:-$PWD/output-$(date +%Y%m%d-%H%M%S)}" +if [ ! -d "$OUTPUTDIR" ]; then + mkdir -p "$OUTPUTDIR" +fi + +# Download the source +VERSION=2.24 +wget -c http://ftp.gnu.org/gnu/binutils/binutils-$VERSION.tar.bz2 + +# Verify the signature +wget -c -q http://ftp.gnu.org/gnu/binutils/binutils-$VERSION.tar.bz2.sig +if ! gpg --verify binutils-$VERSION.tar.bz2.sig; then + echo "GPG Signature failed to verify." + echo "" + echo "You may need to import the vendor GPG key with:" + echo "# gpg --keyserver pgp.mit.edu --recv-key 4AE55E93" + exit 1 +fi + + +if [ ! -d binutils-$VERSION ]; then + # Extract the source + tar jxf binutils-$VERSION.tar.bz2 + + # Patch the source + ( + cd binutils-$VERSION + patch -p1 < ../ehframe-race.patch + patch -p1 < ../unlock-thin.patch + patch -p1 < ../plugin-dso-fix.patch + ) +fi + +for ARCH in i386 amd64; do + if [ ! -d lucid-chroot-$ARCH ]; then + # Refresh sudo credentials + sudo -v + + # Create the chroot + echo "" + echo "Building chroot for $ARCH" + echo "=============================" + sudo debootstrap \ + --arch=$ARCH \ + --include=build-essential,flex,bison \ + lucid lucid-chroot-$ARCH + echo "=============================" + fi + + BUILDDIR=lucid-chroot-$ARCH/build + + # Clean up any previous failed build attempts inside chroot + if [ -d "$BUILDDIR" ]; then + sudo rm -rf "$BUILDDIR" + fi + + # Copy data into the chroot + sudo mkdir -p "$BUILDDIR" + sudo cp -a binutils-$VERSION "$BUILDDIR" + sudo cp -a build-one.sh "$BUILDDIR" + + # Do the build + PREFIX= + case $ARCH in + i386) + PREFIX="setarch linux32" + ARCHNAME=i686-pc-linux-gnu + ;; + amd64) + PREFIX="setarch linux64" + ARCHNAME=x86_64-unknown-linux-gnu + ;; + esac + echo "" + echo "Building binutils for $ARCH" + LOGFILE="$OUTPUTDIR/build-$ARCH.log" + if ! sudo $PREFIX chroot lucid-chroot-$ARCH /build/build-one.sh /build/binutils-$VERSION > $LOGFILE 2>&1; then + echo "Build failed! See $LOGFILE for details." + exit 1 + fi + + # Copy data out of the chroot + sudo chown -R $(whoami) "$BUILDDIR/output/" + + # Strip the output binaries + for i in "$BUILDDIR/output/$ARCHNAME/bin/*"; do + strip $i + done + + # Copy them out of the chroot + cp -a "$BUILDDIR/output/$ARCHNAME" "$OUTPUTDIR" + + # Copy plugin header out of the chroot + mkdir "$OUTPUTDIR/$ARCHNAME/include" + cp "$BUILDDIR/binutils-$VERSION/include/plugin-api.h" "$OUTPUTDIR/$ARCHNAME/include/" + + # Clean up chroot + sudo rm -rf "$BUILDDIR" +done + +echo "Check you are happy with the binaries in" +echo " $OUTPUTDIR" +echo "Then" +echo " * upload to Google Storage using the upload.sh script" +echo " * roll dependencies" diff --git a/engine/src/flutter/third_party/binutils/build-one.sh b/engine/src/flutter/third_party/binutils/build-one.sh new file mode 100755 index 0000000000..1ae62d10c7 --- /dev/null +++ b/engine/src/flutter/third_party/binutils/build-one.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# Copyright 2014 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. + +# Script to build binutils found in /build/binutils-XXXX when inside a chroot. +# Don't call this script yourself, instead use the build-all.sh script. + +set -e + +if [ -z "$1" ]; then + echo "Directory of binutils not given." + exit 1 +fi + +cd "$1" +./configure --enable-gold=default --enable-threads --enable-plugins \ + --prefix=/build/output +make -j8 all +make install diff --git a/engine/src/flutter/third_party/binutils/download.py b/engine/src/flutter/third_party/binutils/download.py new file mode 100755 index 0000000000..6a760dfd06 --- /dev/null +++ b/engine/src/flutter/third_party/binutils/download.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# Copyright 2014 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. +# vim: set ts=2 sw=2 et sts=2 ai: + +"""Minimal tool to download binutils from Google storage. + +TODO(mithro): Replace with generic download_and_extract tool. +""" + +import os +import platform +import re +import shutil +import subprocess +import sys + + +BINUTILS_DIR = os.path.abspath(os.path.dirname(__file__)) +BINUTILS_FILE = 'binutils.tar.bz2' +BINUTILS_TOOLS = ['bin/ld.gold', 'bin/objcopy', 'bin/objdump'] +BINUTILS_OUT = 'Release' + +DETECT_HOST_ARCH = os.path.abspath(os.path.join( + BINUTILS_DIR, '../../build/detect_host_arch.py')) + + +def ReadFile(filename): + with file(filename, 'r') as f: + return f.read().strip() + + +def WriteFile(filename, content): + assert not os.path.exists(filename) + with file(filename, 'w') as f: + f.write(content) + f.write('\n') + + +def GetArch(): + gyp_host_arch = re.search( + 'host_arch=(\S*)', os.environ.get('GYP_DEFINES', '')) + if gyp_host_arch: + arch = gyp_host_arch.group(1) + # This matches detect_host_arch.py. + if arch == 'x86_64': + return 'x64' + return arch + + return subprocess.check_output(['python', DETECT_HOST_ARCH]).strip() + + +def FetchAndExtract(arch): + archdir = os.path.join(BINUTILS_DIR, 'Linux_' + arch) + tarball = os.path.join(archdir, BINUTILS_FILE) + outdir = os.path.join(archdir, BINUTILS_OUT) + + sha1file = tarball + '.sha1' + if not os.path.exists(sha1file): + print "WARNING: No binutils found for your architecture (%s)!" % arch + return 0 + + checksum = ReadFile(sha1file) + + stampfile = tarball + '.stamp' + if os.path.exists(stampfile): + if (os.path.exists(tarball) and + os.path.exists(outdir) and + checksum == ReadFile(stampfile)): + return 0 + else: + os.unlink(stampfile) + + print "Downloading", tarball + subprocess.check_call([ + 'download_from_google_storage', + '--no_resume', + '--no_auth', + '--bucket', 'chromium-binutils', + '-s', sha1file]) + assert os.path.exists(tarball) + + if os.path.exists(outdir): + shutil.rmtree(outdir) + assert not os.path.exists(outdir) + os.makedirs(outdir) + assert os.path.exists(outdir) + + print "Extracting", tarball + subprocess.check_call(['tar', 'axf', tarball], cwd=outdir) + + for tool in BINUTILS_TOOLS: + assert os.path.exists(os.path.join(outdir, tool)) + + WriteFile(stampfile, checksum) + return 0 + + +def main(args): + if not sys.platform.startswith('linux'): + return 0 + + arch = GetArch() + if arch == 'x64': + return FetchAndExtract(arch) + if arch == 'ia32': + ret = FetchAndExtract(arch) + if ret != 0: + return ret + # Fetch the x64 toolchain as well for official bots with 64-bit kernels. + return FetchAndExtract('x64') + print "Host architecture %s is not supported." % arch + return 1 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/third_party/binutils/ehframe-race.patch b/engine/src/flutter/third_party/binutils/ehframe-race.patch new file mode 100644 index 0000000000..ab0d01eaaf --- /dev/null +++ b/engine/src/flutter/third_party/binutils/ehframe-race.patch @@ -0,0 +1,96 @@ +diff --git a/gold/gold.cc b/gold/gold.cc +index 4833aec..bdfb33d 100644 +--- a/gold/gold.cc ++++ b/gold/gold.cc +@@ -808,6 +808,8 @@ queue_final_tasks(const General_options& options, + if (!any_postprocessing_sections) + { + input_sections_blocker = new Task_token(true); ++ // Write_symbols_task, Relocate_tasks. ++ input_sections_blocker->add_blocker(); + input_sections_blocker->add_blockers(input_objects->number_of_relobjs()); + } + +@@ -836,6 +838,7 @@ queue_final_tasks(const General_options& options, + + // Queue a task to write out the output sections. + workqueue->queue(new Write_sections_task(layout, of, output_sections_blocker, ++ input_sections_blocker, + final_blocker)); + + // Queue a task to write out everything else. +diff --git a/gold/layout.cc b/gold/layout.cc +index 82db775..ef0a879 100644 +--- a/gold/layout.cc ++++ b/gold/layout.cc +@@ -5532,6 +5532,8 @@ void + Write_sections_task::locks(Task_locker* tl) + { + tl->add(this, this->output_sections_blocker_); ++ if (this->input_sections_blocker_ != NULL) ++ tl->add(this, this->input_sections_blocker_); + tl->add(this, this->final_blocker_); + } + +diff --git a/gold/layout.h b/gold/layout.h +index 7c0113c..032f5f3 100644 +--- a/gold/layout.h ++++ b/gold/layout.h +@@ -1454,9 +1454,11 @@ class Write_sections_task : public Task + public: + Write_sections_task(const Layout* layout, Output_file* of, + Task_token* output_sections_blocker, ++ Task_token* input_sections_blocker, + Task_token* final_blocker) + : layout_(layout), of_(of), + output_sections_blocker_(output_sections_blocker), ++ input_sections_blocker_(input_sections_blocker), + final_blocker_(final_blocker) + { } + +@@ -1481,6 +1483,7 @@ class Write_sections_task : public Task + const Layout* layout_; + Output_file* of_; + Task_token* output_sections_blocker_; ++ Task_token* input_sections_blocker_; + Task_token* final_blocker_; + }; + +diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am +index dd06d5f..35cd013 100644 +--- a/gold/testsuite/Makefile.am ++++ b/gold/testsuite/Makefile.am +@@ -2380,10 +2380,9 @@ endif DEFAULT_TARGET_X86_64 + if DEFAULT_TARGET_X86_64 + check_PROGRAMS += exception_x86_64_bnd_test + exception_x86_64_bnd_test_SOURCES = exception_test_main.cc +-exception_x86_64_bnd_test_DEPENDENCIES = exception_x86_64_bnd_1.o \ +- exception_x86_64_bnd_2.o ++exception_x86_64_bnd_test_DEPENDENCIES = gcctestdir/ld exception_x86_64_bnd_1.o exception_x86_64_bnd_2.o + exception_x86_64_bnd_test_LDFLAGS = $(exception_test_LDFLAGS) +-exception_x86_64_bnd_test_LDADD = $(exception_x86_64_bnd_test_DEPENDENCIES) ++exception_x86_64_bnd_test_LDADD = exception_x86_64_bnd_1.o exception_x86_64_bnd_2.o + exception_x86_64_bnd_1.o: exception_test_1.cc gcctestdir/as + $(CXXCOMPILE) -c -fpic -Bgcctestdir/ -Wa,-madd-bnd-prefix -o $@ $< + exception_x86_64_bnd_2.o: exception_test_2.cc gcctestdir/as +diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in +index fed610f..8fbb644 100644 +--- a/gold/testsuite/Makefile.in ++++ b/gold/testsuite/Makefile.in +@@ -2732,11 +2732,9 @@ LDADD = libgoldtest.a ../libgold.a ../../libiberty/libiberty.a $(LIBINTL) \ + @GCC_TRUE@@NATIVE_LINKER_TRUE@ehdr_start_test_5_LDFLAGS = -Bgcctestdir/ + @GCC_TRUE@@NATIVE_LINKER_TRUE@ehdr_start_test_5_LDADD = + @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_x86_64_bnd_test_SOURCES = exception_test_main.cc +-@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_x86_64_bnd_test_DEPENDENCIES = exception_x86_64_bnd_1.o \ +-@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ exception_x86_64_bnd_2.o +- ++@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_x86_64_bnd_test_DEPENDENCIES = gcctestdir/ld exception_x86_64_bnd_1.o exception_x86_64_bnd_2.o + @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_x86_64_bnd_test_LDFLAGS = $(exception_test_LDFLAGS) +-@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_x86_64_bnd_test_LDADD = $(exception_x86_64_bnd_test_DEPENDENCIES) ++@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_x86_64_bnd_test_LDADD = exception_x86_64_bnd_1.o exception_x86_64_bnd_2.o + @DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200 + @DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200 + all: $(BUILT_SOURCES) +-- +1.7.1 + diff --git a/engine/src/flutter/third_party/binutils/plugin-dso-fix.patch b/engine/src/flutter/third_party/binutils/plugin-dso-fix.patch new file mode 100644 index 0000000000..558975686d --- /dev/null +++ b/engine/src/flutter/third_party/binutils/plugin-dso-fix.patch @@ -0,0 +1,97 @@ +commit 3c537f7fdb11f02f7082749f3f21dfdd2c2025e8 +Author: Peter Collingbourne +Date: Wed Feb 4 09:47:28 2015 -0800 + + Resolve forwarding symbols in plugins. + + 2015-02-04 Peter Collingbourne + + * plugin.cc (Pluginobj::get_symbol_resolution_info): Resolve + forwarding symbols when computing symbol resolution info for plugins. + +diff --git a/gold/plugin.cc b/gold/plugin.cc +index bde8c78..68da8e3 100644 +--- a/gold/plugin.cc ++++ b/gold/plugin.cc +@@ -914,7 +914,8 @@ is_visible_from_outside(Symbol* lsym) + // Get symbol resolution info. + + ld_plugin_status +-Pluginobj::get_symbol_resolution_info(int nsyms, ++Pluginobj::get_symbol_resolution_info(Symbol_table* symtab, ++ int nsyms, + ld_plugin_symbol* syms, + int version) const + { +@@ -943,6 +944,8 @@ Pluginobj::get_symbol_resolution_info(int nsyms, + { + ld_plugin_symbol* isym = &syms[i]; + Symbol* lsym = this->symbols_[i]; ++ if (lsym->is_forwarder()) ++ lsym = symtab->resolve_forwards(lsym); + ld_plugin_symbol_resolution res = LDPR_UNKNOWN; + + if (lsym->is_undefined()) +@@ -1511,14 +1514,16 @@ static enum ld_plugin_status + get_symbols(const void* handle, int nsyms, ld_plugin_symbol* syms) + { + gold_assert(parameters->options().has_plugins()); +- Object* obj = parameters->options().plugins()->object( ++ Plugin_manager* plugins = parameters->options().plugins(); ++ Object* obj = plugins->object( + static_cast(reinterpret_cast(handle))); + if (obj == NULL) + return LDPS_ERR; + Pluginobj* plugin_obj = obj->pluginobj(); + if (plugin_obj == NULL) + return LDPS_ERR; +- return plugin_obj->get_symbol_resolution_info(nsyms, syms, 1); ++ Symbol_table* symtab = plugins->symtab(); ++ return plugin_obj->get_symbol_resolution_info(symtab, nsyms, syms, 1); + } + + // Version 2 of the above. The only difference is that this version +@@ -1528,14 +1533,16 @@ static enum ld_plugin_status + get_symbols_v2(const void* handle, int nsyms, ld_plugin_symbol* syms) + { + gold_assert(parameters->options().has_plugins()); +- Object* obj = parameters->options().plugins()->object( ++ Plugin_manager* plugins = parameters->options().plugins(); ++ Object* obj = plugins->object( + static_cast(reinterpret_cast(handle))); + if (obj == NULL) + return LDPS_ERR; + Pluginobj* plugin_obj = obj->pluginobj(); + if (plugin_obj == NULL) + return LDPS_ERR; +- return plugin_obj->get_symbol_resolution_info(nsyms, syms, 2); ++ Symbol_table* symtab = plugins->symtab(); ++ return plugin_obj->get_symbol_resolution_info(symtab, nsyms, syms, 2); + } + + // Add a new (real) input file generated by a plugin. +diff --git a/gold/plugin.h b/gold/plugin.h +index ef78b84..f926879 100644 +--- a/gold/plugin.h ++++ b/gold/plugin.h +@@ -282,6 +282,10 @@ class Plugin_manager + input_objects() const + { return this->input_objects_; } + ++ Symbol_table* ++ symtab() ++ { return this->symtab_; } ++ + Layout* + layout() + { return this->layout_; } +@@ -396,7 +400,8 @@ class Pluginobj : public Object + + // Fill in the symbol resolution status for the given plugin symbols. + ld_plugin_status +- get_symbol_resolution_info(int nsyms, ++ get_symbol_resolution_info(Symbol_table* symtab, ++ int nsyms, + ld_plugin_symbol* syms, + int version) const; + diff --git a/engine/src/flutter/third_party/binutils/unlock-thin.patch b/engine/src/flutter/third_party/binutils/unlock-thin.patch new file mode 100644 index 0000000000..6a4b6a7c98 --- /dev/null +++ b/engine/src/flutter/third_party/binutils/unlock-thin.patch @@ -0,0 +1,129 @@ +commit 2cfbf2fece582c29df348104b28677c38a8301f4 +Author: Cary Coutant +Date: Tue Feb 3 19:54:57 2015 -0800 + + Fix a file descriptor leak in gold. + + When an LTO linker plugin claims an external member of a thin archive, gold + does not properly unlock the file and make its file descriptor available for + reuse. This patch fixes the problem by modifying Archive::include_member to + unlock the object file via an RAII class instance, ensuring that it will be + unlocked no matter what path is taken through the function. + + gold/ + PR gold/15660 + * archive.cc (Thin_archive_object_unlocker): New class. + (Archive::include_member): Unlock external members of thin archives. + * testsuite/Makefile.am (plugin_test_1): Rename .syms files. + (plugin_test_2): Likewise. + (plugin_test_3): Likewise. + (plugin_test_4): Likewise. + (plugin_test_5): Likewise. + (plugin_test_6): Likewise. + (plugin_test_7): Likewise. + (plugin_test_8): Likewise. + (plugin_test_9): Likewise. + (plugin_test_10): Likewise. + (plugin_test_11): New test case. + * testsuite/Makefile.in: Regenerate. + * testsuite/plugin_test.c (claim_file_hook): Check for parallel .syms + file to decide whether to claim file. + (all_symbols_read_hook): Likewise. + * testsuite/plugin_test_1.sh: Adjust expected output. + * testsuite/plugin_test_2.sh: Likewise. + * testsuite/plugin_test_3.sh: Likewise. + * testsuite/plugin_test_6.sh: Likewise. + * testsuite/plugin_test_tls.sh: Likewise. + * testsuite/plugin_test_11.sh: New testcase. + +diff --git a/gold/archive.cc b/gold/archive.cc +index 69107f5..6d25980 100644 +--- a/gold/archive.cc ++++ b/gold/archive.cc +@@ -930,6 +930,32 @@ Archive::count_members() + return ret; + } + ++// RAII class to ensure we unlock the object if it's a member of a ++// thin archive. We can't use Task_lock_obj in Archive::include_member ++// because the object file is already locked when it's opened by ++// get_elf_object_for_member. ++ ++class Thin_archive_object_unlocker ++{ ++ public: ++ Thin_archive_object_unlocker(const Task *task, Object* obj) ++ : task_(task), obj_(obj) ++ { } ++ ++ ~Thin_archive_object_unlocker() ++ { ++ if (this->obj_->offset() == 0) ++ this->obj_->unlock(this->task_); ++ } ++ ++ private: ++ Thin_archive_object_unlocker(const Thin_archive_object_unlocker&); ++ Thin_archive_object_unlocker& operator=(const Thin_archive_object_unlocker&); ++ ++ const Task* task_; ++ Object* obj_; ++}; ++ + // Include an archive member in the link. OFF is the file offset of + // the member header. WHY is the reason we are including this member. + // Return true if we added the member or if we had an error, return +@@ -978,6 +1004,10 @@ Archive::include_member(Symbol_table* symtab, Layout* layout, + return unconfigured ? false : true; + } + ++ // If the object is an external member of a thin archive, ++ // unlock it when we're done here. ++ Thin_archive_object_unlocker unlocker(this->task_, obj); ++ + if (mapfile != NULL) + mapfile->report_include_archive_member(obj->name(), sym, why); + +@@ -991,31 +1021,21 @@ Archive::include_member(Symbol_table* symtab, Layout* layout, + + if (!input_objects->add_object(obj)) + { +- // If this is an external member of a thin archive, unlock the +- // file. +- if (obj->offset() == 0) +- obj->unlock(this->task_); + delete obj; ++ return true; + } +- else +- { +- { +- if (layout->incremental_inputs() != NULL) +- layout->incremental_inputs()->report_object(obj, 0, this, NULL); +- Read_symbols_data sd; +- obj->read_symbols(&sd); +- obj->layout(symtab, layout, &sd); +- obj->add_symbols(symtab, &sd, layout); +- } +- +- // If this is an external member of a thin archive, unlock the file +- // for the next task. +- if (obj->offset() == 0) +- obj->unlock(this->task_); + +- this->included_member_ = true; +- } ++ if (layout->incremental_inputs() != NULL) ++ layout->incremental_inputs()->report_object(obj, 0, this, NULL); ++ ++ { ++ Read_symbols_data sd; ++ obj->read_symbols(&sd); ++ obj->layout(symtab, layout, &sd); ++ obj->add_symbols(symtab, &sd, layout); ++ } + ++ this->included_member_ = true; + return true; + } + diff --git a/engine/src/flutter/third_party/binutils/upload.sh b/engine/src/flutter/third_party/binutils/upload.sh new file mode 100755 index 0000000000..5ff10aad3a --- /dev/null +++ b/engine/src/flutter/third_party/binutils/upload.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Copyright 2014 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. + +# Upload the generated output to Google storage. + +set -e + +if [ ! -d "$1" ]; then + echo "update.sh " + exit 1 +fi + +if echo "$PWD" | grep -qE "/src/third_party/binutils$"; then + echo -n +else + echo "update.sh should be run in src/third_party/binutils" + exit 1 +fi + +if [ ! -f ~/.boto ]; then + echo "You need to run 'gsutil config' to set up authentication before running this script." + exit 1 +fi + +for DIR in $1/*; do + # Skip if not directory + if [ ! -d "$DIR" ]; then + continue + fi + + case "$DIR" in + */i686-pc-linux-gnu) + export ARCH="Linux_ia32" + ;; + + */x86_64-unknown-linux-gnu) + export ARCH="Linux_x64" + ;; + + *) + echo "Unknown architecture directory $DIR" + exit 1 + ;; + esac + + if [ ! -d "$ARCH" ]; then + mkdir -p "$ARCH" + fi + + BINUTILS_TAR_BZ2="$ARCH/binutils.tar.bz2" + FULL_BINUTILS_TAR_BZ2="$PWD/$BINUTILS_TAR_BZ2" + if [ -f "${BINUTILS_TAR_BZ2}.sha1" ]; then + rm "${BINUTILS_TAR_BZ2}.sha1" + fi + (cd "$DIR"; tar jcf "$FULL_BINUTILS_TAR_BZ2" .) + + upload_to_google_storage.py --bucket chromium-binutils "$BINUTILS_TAR_BZ2" + git add -f "${BINUTILS_TAR_BZ2}.sha1" +done + +echo "Please commit the new .sha1 to the Chromium repository" +echo "" +echo "# git commit" diff --git a/engine/src/flutter/third_party/dart-pkg/BUILD.gn b/engine/src/flutter/third_party/dart-pkg/BUILD.gn new file mode 100644 index 0000000000..8f7c873b49 --- /dev/null +++ b/engine/src/flutter/third_party/dart-pkg/BUILD.gn @@ -0,0 +1,75 @@ +# Copyright 2015 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. + +import("//mojo/public/dart/rules.gni") + +group("dart-pkg") { + deps = [ + ":archive", + ":args", + ":box2d", + ":cassowary", + ":collection", + ":crypto", + ":newton", + ":path", + ":quiver", + ":source_span", + ":string_scanner", + ":vector_math", + ":yaml", + ] +} + +dart_pkg("archive") { + pkg_dir = "//third_party/dart-pkg/archive" +} + +dart_pkg("args") { + pkg_dir = "//third_party/dart-pkg/args" +} + +dart_pkg("box2d") { + pkg_dir = "//third_party/dart-pkg/box2d" +} + +dart_pkg("cassowary") { + pkg_dir = "//third_party/dart-pkg/cassowary" +} + +dart_pkg("collection") { + pkg_dir = "//third_party/dart-pkg/collection" +} + +dart_pkg("crypto") { + pkg_dir = "//third_party/dart-pkg/crypto" +} + +dart_pkg("newton") { + pkg_dir = "//third_party/dart-pkg/newton" +} + +dart_pkg("path") { + pkg_dir = "//third_party/dart-pkg/path" +} + +dart_pkg("quiver") { + pkg_dir = "//third_party/dart-pkg/quiver" +} + +dart_pkg("source_span") { + pkg_dir = "//third_party/dart-pkg/source_span" +} + +dart_pkg("string_scanner") { + pkg_dir = "//third_party/dart-pkg/string_scanner" +} + +dart_pkg("vector_math") { + pkg_dir = "//third_party/dart-pkg/vector_math" +} + +dart_pkg("yaml") { + pkg_dir = "//third_party/dart-pkg/yaml" +} diff --git a/engine/src/flutter/third_party/freetype-android/BUILD.gn b/engine/src/flutter/third_party/freetype-android/BUILD.gn new file mode 100644 index 0000000000..67f6fe8da7 --- /dev/null +++ b/engine/src/flutter/third_party/freetype-android/BUILD.gn @@ -0,0 +1,50 @@ +# Copyright 2014 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. + +assert(is_android, "This library is only used on Android") + +config("freetype_config") { + include_dirs = [ "src/include" ] +} + +source_set("freetype") { + sources = [ + # The following files are not sorted alphabetically, but in the + # same order as in Android.mk to ease maintenance. + "src/src/base/ftbbox.c", + "src/src/base/ftbitmap.c", + "src/src/base/ftfstype.c", + "src/src/base/ftglyph.c", + "src/src/base/ftlcdfil.c", + "src/src/base/ftstroke.c", + "src/src/base/fttype1.c", + "src/src/base/ftxf86.c", + "src/src/base/ftbase.c", + "src/src/base/ftsystem.c", + "src/src/base/ftinit.c", + "src/src/base/ftgasp.c", + "src/src/base/ftmm.c", + "src/src/gzip/ftgzip.c", + "src/src/raster/raster.c", + "src/src/sfnt/sfnt.c", + "src/src/smooth/smooth.c", + "src/src/autofit/autofit.c", + "src/src/truetype/truetype.c", + "src/src/cff/cff.c", + "src/src/psnames/psnames.c", + "src/src/pshinter/pshinter.c", + ] + + defines = [ + "FT2_BUILD_LIBRARY", + "DARWIN_NO_CARBON", + ] + + public_configs = [ ":freetype_config" ] + + deps = [ + "//third_party/libpng", + "//third_party/zlib", + ] +} diff --git a/engine/src/flutter/third_party/freetype-android/README.chromium b/engine/src/flutter/third_party/freetype-android/README.chromium new file mode 100644 index 0000000000..45032d2778 --- /dev/null +++ b/engine/src/flutter/third_party/freetype-android/README.chromium @@ -0,0 +1,18 @@ +Name: Freetype +URL: https://android.googlesource.com/platform/external/freetype/ +Version: 9c745321260bb728ab1cd1c8fd5f075854b2ad49 +License: Custom license "inspired by the BSD, Artistic, and IJG (Independent + JPEG Group) licenses" +License File: src/NOTICE +Security Critical: yes +License Android Compatible: yes + +Description: +This package was copied from Android source tree and just used for Android. +For other platforms, the freetype is normally found outside the Chromium tree; +e.g. on Mac it's in /usr/include + +How to update: +1. Follow the directions in src/README.chromium to update the source. +2. Update freetype.gyp and BUILD.gn to reflect any changes in src/Android.mk. +3. Commit build changes while rolling Chromium DEPS to new commit. diff --git a/engine/src/flutter/third_party/iccjpeg/BUILD.gn b/engine/src/flutter/third_party/iccjpeg/BUILD.gn new file mode 100644 index 0000000000..4c4a995977 --- /dev/null +++ b/engine/src/flutter/third_party/iccjpeg/BUILD.gn @@ -0,0 +1,19 @@ +# Copyright 2014 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. + +config("iccjpeg_config") { + include_dirs = [ "." ] +} + +source_set("iccjpeg") { + sources = [ + "iccjpeg.c", + "iccjpeg.h", + ] + + public_configs = [ ":iccjpeg_config" ] + deps = [ + "//third_party:jpeg", + ] +} diff --git a/engine/src/flutter/third_party/iccjpeg/LICENSE b/engine/src/flutter/third_party/iccjpeg/LICENSE new file mode 100644 index 0000000000..49b65d36bf --- /dev/null +++ b/engine/src/flutter/third_party/iccjpeg/LICENSE @@ -0,0 +1,49 @@ +LICENSE extracted from IJG's jpeg distribution: +----------------------------------------------- + +In plain English: + +1. We don't promise that this software works. (But if you find any bugs, + please let us know!) +2. You can use this software for whatever you want. You don't have to pay us. +3. You may not pretend that you wrote this software. If you use it in a + program, you must acknowledge somewhere in your documentation that + you've used the IJG code. + +In legalese: + +The authors make NO WARRANTY or representation, either express or implied, +with respect to this software, its quality, accuracy, merchantability, or +fitness for a particular purpose. This software is provided "AS IS", and you, +its user, assume the entire risk as to its quality and accuracy. + +This software is copyright (C) 1991-1998, Thomas G. Lane. +All Rights Reserved except as specified below. + +Permission is hereby granted to use, copy, modify, and distribute this +software (or portions thereof) for any purpose, without fee, subject to these +conditions: +(1) If any part of the source code for this software is distributed, then this +README file must be included, with this copyright and no-warranty notice +unaltered; and any additions, deletions, or changes to the original files +must be clearly indicated in accompanying documentation. +(2) If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the work of +the Independent JPEG Group". +(3) Permission for use of this software is granted only if the user accepts +full responsibility for any undesirable consequences; the authors accept +NO LIABILITY for damages of any kind. + +These conditions apply to any software derived from or based on the IJG code, +not just to the unmodified library. If you use our work, you ought to +acknowledge us. + +Permission is NOT granted for the use of any IJG author's name or company name +in advertising or publicity relating to this software or products derived from +it. This software may be referred to only as "the Independent JPEG Group's +software". + +We specifically permit and encourage the use of this software as the basis of +commercial products, provided that all warranty or liability claims are +assumed by the product vendor. + diff --git a/engine/src/flutter/third_party/iccjpeg/OWNERS b/engine/src/flutter/third_party/iccjpeg/OWNERS new file mode 100644 index 0000000000..082e626289 --- /dev/null +++ b/engine/src/flutter/third_party/iccjpeg/OWNERS @@ -0,0 +1 @@ +fbarchard@chromium.org diff --git a/engine/src/flutter/third_party/iccjpeg/README.chromium b/engine/src/flutter/third_party/iccjpeg/README.chromium new file mode 100644 index 0000000000..5722ecca99 --- /dev/null +++ b/engine/src/flutter/third_party/iccjpeg/README.chromium @@ -0,0 +1,20 @@ +Name: iccjpeg +URL: http://www.ijg.org +Version: unknown +License: Custom license +License File: LICENSE +Security Critical: yes +License Android Compatible: yes + +Description: +Contain support for ICC color profile on top of jpeg6b (and up) library. +Original author is Tom Lane, from the IJG group. +We include the relevant part of the original IJG licensing term +in the file LICENSE. + +Documentation for ICC profile can be found at: http://www.color.org + +Local Modifications: +* On BSD platforms we might need to include to correct header file from + the system, so a USE_SYSTEM_LIBJPEG ifdef is added to iccjpeg.h in order + to be able to decide which jpeglib.h header to include. diff --git a/engine/src/flutter/third_party/iccjpeg/iccjpeg.c b/engine/src/flutter/third_party/iccjpeg/iccjpeg.c new file mode 100644 index 0000000000..aa96c8bebf --- /dev/null +++ b/engine/src/flutter/third_party/iccjpeg/iccjpeg.c @@ -0,0 +1,248 @@ +/* + * iccprofile.c + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. If you need to do that, + * change all the "unsigned int" variables to "INT32". You'll also need + * to find a malloc() replacement that can allocate more than 64K. + */ + +#include "iccjpeg.h" +#include /* define malloc() */ + + +/* + * Since an ICC profile can be larger than the maximum size of a JPEG marker + * (64K), we need provisions to split it into multiple markers. The format + * defined by the ICC specifies one or more APP2 markers containing the + * following data: + * Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + * Marker sequence number 1 for first APP2, 2 for next, etc (1 byte) + * Number of markers Total number of APP2's used (1 byte) + * Profile data (remainder of APP2 data) + * Decoders should use the marker sequence numbers to reassemble the profile, + * rather than assuming that the APP2 markers appear in the correct sequence. + */ + +#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ +#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ +#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ +#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) + + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +void +write_icc_profile (j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len) +{ + unsigned int num_markers; /* total number of markers we'll write */ + int cur_marker = 1; /* per spec, counting starts at 1 */ + unsigned int length; /* number of bytes to write in this marker */ + + /* Calculate the number of markers we'll need, rounding up of course */ + num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER; + if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len) + num_markers++; + + while (icc_data_len > 0) { + /* length of profile to put in this marker */ + length = icc_data_len; + if (length > MAX_DATA_BYTES_IN_MARKER) + length = MAX_DATA_BYTES_IN_MARKER; + icc_data_len -= length; + + /* Write the JPEG marker header (APP2 code and marker length) */ + jpeg_write_m_header(cinfo, ICC_MARKER, + (unsigned int) (length + ICC_OVERHEAD_LEN)); + + /* Write the marker identifying string "ICC_PROFILE" (null-terminated). + * We code it in this less-than-transparent way so that the code works + * even if the local character set is not ASCII. + */ + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x5F); + jpeg_write_m_byte(cinfo, 0x50); + jpeg_write_m_byte(cinfo, 0x52); + jpeg_write_m_byte(cinfo, 0x4F); + jpeg_write_m_byte(cinfo, 0x46); + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x4C); + jpeg_write_m_byte(cinfo, 0x45); + jpeg_write_m_byte(cinfo, 0x0); + + /* Add the sequencing info */ + jpeg_write_m_byte(cinfo, cur_marker); + jpeg_write_m_byte(cinfo, (int) num_markers); + + /* Add the profile data */ + while (length--) { + jpeg_write_m_byte(cinfo, *icc_data_ptr); + icc_data_ptr++; + } + cur_marker++; + } +} + + +/* + * Prepare for reading an ICC profile + */ + +void +setup_read_icc_profile (j_decompress_ptr cinfo) +{ + /* Tell the library to keep any APP2 data it may find */ + jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF); +} + + +/* + * Handy subroutine to test whether a saved marker is an ICC profile marker. + */ + +static boolean +marker_is_icc (jpeg_saved_marker_ptr marker) +{ + return + marker->marker == ICC_MARKER && + marker->data_length >= ICC_OVERHEAD_LEN && + /* verify the identifying string */ + GETJOCTET(marker->data[0]) == 0x49 && + GETJOCTET(marker->data[1]) == 0x43 && + GETJOCTET(marker->data[2]) == 0x43 && + GETJOCTET(marker->data[3]) == 0x5F && + GETJOCTET(marker->data[4]) == 0x50 && + GETJOCTET(marker->data[5]) == 0x52 && + GETJOCTET(marker->data[6]) == 0x4F && + GETJOCTET(marker->data[7]) == 0x46 && + GETJOCTET(marker->data[8]) == 0x49 && + GETJOCTET(marker->data[9]) == 0x4C && + GETJOCTET(marker->data[10]) == 0x45 && + GETJOCTET(marker->data[11]) == 0x0; +} + + +/* + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + * + * NOTE: if the file contains invalid ICC APP2 markers, we just silently + * return FALSE. You might want to issue an error message instead. + */ + +boolean +read_icc_profile (j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len) +{ + jpeg_saved_marker_ptr marker; + int num_markers = 0; + int seq_no; + JOCTET *icc_data; + unsigned int total_length; +#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */ + char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */ + unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */ + unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */ + + *icc_data_ptr = NULL; /* avoid confusion if FALSE return */ + *icc_data_len = 0; + + /* This first pass over the saved markers discovers whether there are + * any ICC markers and verifies the consistency of the marker numbering. + */ + + for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++) + marker_present[seq_no] = 0; + + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + if (num_markers == 0) + num_markers = GETJOCTET(marker->data[13]); + else if (num_markers != GETJOCTET(marker->data[13])) + return FALSE; /* inconsistent num_markers fields */ + seq_no = GETJOCTET(marker->data[12]); + if (seq_no <= 0 || seq_no > num_markers) + return FALSE; /* bogus sequence number */ + if (marker_present[seq_no]) + return FALSE; /* duplicate sequence numbers */ + marker_present[seq_no] = 1; + data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN; + } + } + + if (num_markers == 0) + return FALSE; + + /* Check for missing markers, count total space needed, + * compute offset of each marker's part of the data. + */ + + total_length = 0; + for (seq_no = 1; seq_no <= num_markers; seq_no++) { + if (marker_present[seq_no] == 0) + return FALSE; /* missing sequence number */ + data_offset[seq_no] = total_length; + total_length += data_length[seq_no]; + } + + if (total_length <= 0) + return FALSE; /* found only empty markers? */ + + /* Allocate space for assembled data */ + icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET)); + if (icc_data == NULL) + return FALSE; /* oops, out of memory */ + + /* and fill it in */ + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + JOCTET FAR *src_ptr; + JOCTET *dst_ptr; + unsigned int length; + seq_no = GETJOCTET(marker->data[12]); + dst_ptr = icc_data + data_offset[seq_no]; + src_ptr = marker->data + ICC_OVERHEAD_LEN; + length = data_length[seq_no]; + while (length--) { + *dst_ptr++ = *src_ptr++; + } + } + } + + *icc_data_ptr = icc_data; + *icc_data_len = total_length; + + return TRUE; +} diff --git a/engine/src/flutter/third_party/iccjpeg/iccjpeg.gyp b/engine/src/flutter/third_party/iccjpeg/iccjpeg.gyp new file mode 100644 index 0000000000..780114858a --- /dev/null +++ b/engine/src/flutter/third_party/iccjpeg/iccjpeg.gyp @@ -0,0 +1,24 @@ +# Copyright (c) 2011 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. + +{ + 'targets': [ + { + 'target_name': 'iccjpeg', + 'type': 'static_library', + 'dependencies': [ + '<(libjpeg_gyp_path):libjpeg', + ], + 'sources': [ + 'iccjpeg.c', + 'iccjpeg.h', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '.', + ], + }, + }, + ], +} diff --git a/engine/src/flutter/third_party/iccjpeg/iccjpeg.h b/engine/src/flutter/third_party/iccjpeg/iccjpeg.h new file mode 100644 index 0000000000..25dd42e737 --- /dev/null +++ b/engine/src/flutter/third_party/iccjpeg/iccjpeg.h @@ -0,0 +1,78 @@ +/* + * iccprofile.h + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. See iccprofile.c + * for details. + */ + +#include /* needed to define "FILE", "NULL" */ + +#if defined(USE_SYSTEM_LIBJPEG) +#include +#else +#include "jpeglib.h" +#endif + + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +extern void write_icc_profile JPP((j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len)); + + +/* + * Reading a JPEG file that may contain an ICC profile requires two steps: + * + * 1. After jpeg_create_decompress() but before jpeg_read_header(), + * call setup_read_icc_profile(). This routine tells the IJG library + * to save in memory any APP2 markers it may find in the file. + * + * 2. After jpeg_read_header(), call read_icc_profile() to find out + * whether there was a profile and obtain it if so. + */ + + +/* + * Prepare for reading an ICC profile + */ + +extern void setup_read_icc_profile JPP((j_decompress_ptr cinfo)); + + +/* + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + */ + +extern boolean read_icc_profile JPP((j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len)); diff --git a/engine/src/flutter/third_party/jsr-305/BUILD.gn b/engine/src/flutter/third_party/jsr-305/BUILD.gn new file mode 100644 index 0000000000..3e24063c2f --- /dev/null +++ b/engine/src/flutter/third_party/jsr-305/BUILD.gn @@ -0,0 +1,37 @@ +import("//build/config/android/rules.gni") + +android_library("jsr_305_javalib") { + java_files = [ + "src/ri/src/main/java/javax/annotation/PropertyKey.java", + "src/ri/src/main/java/javax/annotation/CheckForSigned.java", + "src/ri/src/main/java/javax/annotation/Detainted.java", + "src/ri/src/main/java/javax/annotation/Untainted.java", + "src/ri/src/main/java/javax/annotation/Signed.java", + "src/ri/src/main/java/javax/annotation/Nonnull.java", + "src/ri/src/main/java/javax/annotation/Nullable.java", + "src/ri/src/main/java/javax/annotation/ParametersAreNullableByDefault.java", + "src/ri/src/main/java/javax/annotation/OverridingMethodsMustInvokeSuper.java", + "src/ri/src/main/java/javax/annotation/Tainted.java", + "src/ri/src/main/java/javax/annotation/CheckReturnValue.java", + "src/ri/src/main/java/javax/annotation/Nonnegative.java", + "src/ri/src/main/java/javax/annotation/WillCloseWhenClosed.java", + "src/ri/src/main/java/javax/annotation/RegEx.java", + "src/ri/src/main/java/javax/annotation/Syntax.java", + "src/ri/src/main/java/javax/annotation/meta/TypeQualifierValidator.java", + "src/ri/src/main/java/javax/annotation/meta/Exclusive.java", + "src/ri/src/main/java/javax/annotation/meta/Exhaustive.java", + "src/ri/src/main/java/javax/annotation/meta/TypeQualifier.java", + "src/ri/src/main/java/javax/annotation/meta/TypeQualifierNickname.java", + "src/ri/src/main/java/javax/annotation/meta/TypeQualifierDefault.java", + "src/ri/src/main/java/javax/annotation/meta/When.java", + "src/ri/src/main/java/javax/annotation/CheckForNull.java", + "src/ri/src/main/java/javax/annotation/MatchesPattern.java", + "src/ri/src/main/java/javax/annotation/concurrent/Immutable.java", + "src/ri/src/main/java/javax/annotation/concurrent/NotThreadSafe.java", + "src/ri/src/main/java/javax/annotation/concurrent/ThreadSafe.java", + "src/ri/src/main/java/javax/annotation/concurrent/GuardedBy.java", + "src/ri/src/main/java/javax/annotation/ParametersAreNonnullByDefault.java", + "src/ri/src/main/java/javax/annotation/WillClose.java", + "src/ri/src/main/java/javax/annotation/WillNotClose.java", + ] +} diff --git a/engine/src/flutter/third_party/jsr-305/README.chromium b/engine/src/flutter/third_party/jsr-305/README.chromium new file mode 100644 index 0000000000..cecbc52be0 --- /dev/null +++ b/engine/src/flutter/third_party/jsr-305/README.chromium @@ -0,0 +1,16 @@ +Name: JSR 305: Annotations for Software Defect Detection in Java +Short Name: jsr-305 +URL: https://code.google.com/p/jsr-305/ +Version: r51 +License: BSD 2-Clause License +License File: src/ri/LICENSE +Security Critical: no + +Description: +This project contains reference implementations, test cases, and other +documents under source code control for Java Specification Request 305: +Annotations for Software Defect Detection. More information at the Google +group: http://groups.google.com/group/jsr-305 + +Local Modifications: +None. diff --git a/engine/src/flutter/third_party/jsr-305/jsr-305.gyp b/engine/src/flutter/third_party/jsr-305/jsr-305.gyp new file mode 100644 index 0000000000..37af4b20a5 --- /dev/null +++ b/engine/src/flutter/third_party/jsr-305/jsr-305.gyp @@ -0,0 +1,19 @@ +# Copyright (c) 2012 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. + +{ + 'targets': [ + { + 'target_name': 'jsr_305_javalib', + 'type': 'none', + 'variables': { + # The sources are not located in a folder that is called src/, so we + # need to set it in additional_src_dirs parameter instead. + 'java_in_dir': '../../build/android/empty', + 'additional_src_dirs': [ 'src/ri/' ], + }, + 'includes': [ '../../build/java.gypi' ], + }, + ] +} diff --git a/engine/src/flutter/third_party/junit/BUILD.gn b/engine/src/flutter/third_party/junit/BUILD.gn new file mode 100644 index 0000000000..babe0f2bdd --- /dev/null +++ b/engine/src/flutter/third_party/junit/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright 2014 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. + +import("//build/config/android/rules.gni") + +# GYP: //third_party/junit.gyp:hamcrest_jar +java_prebuilt("hamcrest") { + jar_path = "src/lib/hamcrest-core-1.1.jar" +} + +# GYP: //third_party/junit.gyp:junit_jar +java_library("junit") { + chromium_code = false + testonly = true + deps = [ + ":hamcrest", + ] + DEPRECATED_java_in_dir = "src/src/main/java" +} diff --git a/engine/src/flutter/third_party/junit/LICENSE b/engine/src/flutter/third_party/junit/LICENSE new file mode 100644 index 0000000000..1aba77acf1 --- /dev/null +++ b/engine/src/flutter/third_party/junit/LICENSE @@ -0,0 +1,218 @@ +JUnit + +Common Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial code and + documentation distributed under this Agreement, and + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are +distributed by that particular Contributor. A Contribution 'originates' from a +Contributor if it was added to the Program by such Contributor itself or anyone +acting on such Contributor's behalf. Contributions do not include additions to +the Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) are +not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free copyright license to +reproduce, prepare derivative works of, publicly display, publicly perform, +distribute and sublicense the Contribution of such Contributor, if any, and +such derivative works, in source code and object code form. + + b) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free patent license under +Licensed Patents to make, use, sell, offer to sell, import and otherwise +transfer the Contribution of such Contributor, if any, in source code and +object code form. This patent license shall apply to the combination of the +Contribution and the Program if, at the time the Contribution is added by the +Contributor, such addition of the Contribution causes such combination to be +covered by the Licensed Patents. The patent license shall not apply to any +other combinations which include the Contribution. No hardware per se is +licensed hereunder. + + c) Recipient understands that although each Contributor grants the +licenses to its Contributions set forth herein, no assurances are provided by +any Contributor that the Program does not infringe the patent or other +intellectual property rights of any other entity. Each Contributor disclaims +any liability to Recipient for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a condition to +exercising the rights and licenses granted hereunder, each Recipient hereby +assumes sole responsibility to secure any other intellectual property rights +needed, if any. For example, if a third party patent license is required to +allow Recipient to distribute the Program, it is Recipient's responsibility to +acquire that license before distributing the Program. + + d) Each Contributor represents that to its knowledge it has sufficient +copyright rights in its Contribution, if any, to grant the copyright license +set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under +its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose; + + ii) effectively excludes on behalf of all Contributors all liability for +damages, including direct, indirect, special, incidental and consequential +damages, such as lost profits; + + iii) states that any provisions which differ from this Agreement are +offered by that Contributor alone and not by any other party; and + + iv) states that source code for the Program is available from such +Contributor, and informs licensees how to obtain it in a reasonable manner on +or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of the +Program. + +Contributors may not remove or alter any copyright notices contained within the +Program. + +Each Contributor must identify itself as the originator of its Contribution, if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, if +a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, damages +and costs (collectively "Losses") arising from claims, lawsuits and other legal +actions brought by a third party against the Indemnified Contributor to the +extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may +participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If that +Commercial Contributor then makes performance claims, or offers warranties +related to Product X, those performance claims and warranties are such +Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a court +requires any other Contributor to pay any damages as a result, the Commercial +Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using +and distributing the Program and assumes all risks associated with its exercise +of rights under this Agreement, including but not limited to the risks and +costs of program errors, compliance with applicable laws, damage to or loss of +data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS +GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable +law, it shall not affect the validity or enforceability of the remainder of the +terms of this Agreement, and without further action by the parties hereto, such +provision shall be reformed to the minimum extent necessary to make such +provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with respect to +a patent applicable to software (including a cross-claim or counterclaim in a +lawsuit), then any patent licenses granted by that Contributor to such +Recipient under this Agreement shall terminate as of the date such litigation +is filed. In addition, if Recipient institutes patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's rights +granted under Section 2(b) shall terminate as of the date such litigation is +filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue +and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to time. +No one other than the Agreement Steward has the right to modify this Agreement. +IBM is the initial Agreement Steward. IBM may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial +in any resulting litigation. + diff --git a/engine/src/flutter/third_party/junit/OWNERS b/engine/src/flutter/third_party/junit/OWNERS new file mode 100644 index 0000000000..212ffdf244 --- /dev/null +++ b/engine/src/flutter/third_party/junit/OWNERS @@ -0,0 +1,3 @@ +jbudorick@chromium.org +klundberg@chromium.org + diff --git a/engine/src/flutter/third_party/junit/README.chromium b/engine/src/flutter/third_party/junit/README.chromium new file mode 100644 index 0000000000..5a1651bb74 --- /dev/null +++ b/engine/src/flutter/third_party/junit/README.chromium @@ -0,0 +1,10 @@ +Name: JUnit +URL: http://junit.org +Version: 4.10 +License: Common Public License 1.0 +License File: NOT_SHIPPED +Security Critical: no +License Android Compatible: yes +Description: JUnit is a java unit testing library. +Local Modifications: None + diff --git a/engine/src/flutter/third_party/junit/junit.gyp b/engine/src/flutter/third_party/junit/junit.gyp new file mode 100644 index 0000000000..c797e06a5e --- /dev/null +++ b/engine/src/flutter/third_party/junit/junit.gyp @@ -0,0 +1,34 @@ +# Copyright 2014 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. + +{ + 'targets': [ + { + # GN: //third_party/junit:hamcrest + 'target_name': 'hamcrest_jar', + 'type': 'none', + 'variables': { + 'jar_path': 'src/lib/hamcrest-core-1.1.jar', + }, + 'includes': [ + '../../build/host_prebuilt_jar.gypi', + ] + }, + { + # GN: //third_party/junit:junit + 'target_name': 'junit_jar', + 'type': 'none', + 'dependencies': [ + 'hamcrest_jar', + ], + 'variables': { + 'src_paths': [ 'src/src/main/java' ], + }, + 'includes': [ + '../../build/host_jar.gypi', + ], + }, + ], +} + diff --git a/engine/src/flutter/third_party/markupsafe/AUTHORS b/engine/src/flutter/third_party/markupsafe/AUTHORS new file mode 100644 index 0000000000..f7e2942ecc --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/AUTHORS @@ -0,0 +1,13 @@ +MarkupSafe is written and maintained by Armin Ronacher and +various contributors: + +Development Lead +```````````````` + +- Armin Ronacher + +Patches and Suggestions +``````````````````````` + +- Georg Brandl +- Mickaël Guérin diff --git a/engine/src/flutter/third_party/markupsafe/LICENSE b/engine/src/flutter/third_party/markupsafe/LICENSE new file mode 100644 index 0000000000..5d2693890d --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/LICENSE @@ -0,0 +1,33 @@ +Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS +for more details. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as well +as documentation, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/engine/src/flutter/third_party/markupsafe/MarkupSafe-0.18.tar.gz.md5 b/engine/src/flutter/third_party/markupsafe/MarkupSafe-0.18.tar.gz.md5 new file mode 100644 index 0000000000..1348d1eea1 --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/MarkupSafe-0.18.tar.gz.md5 @@ -0,0 +1 @@ +f8d252fd05371e51dec2fe9a36890687 MarkupSafe-0.18.tar.gz diff --git a/engine/src/flutter/third_party/markupsafe/MarkupSafe-0.18.tar.gz.sha512 b/engine/src/flutter/third_party/markupsafe/MarkupSafe-0.18.tar.gz.sha512 new file mode 100644 index 0000000000..ab752200d5 --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/MarkupSafe-0.18.tar.gz.sha512 @@ -0,0 +1 @@ +0438ddf0fdab465c40d9afba8c14ad346be0868df654c11130d05e329992d456a9bc278551970cbd09244a29c77213885d0c363c951b0cfd4d9aa95b248ecff5 MarkupSafe-0.18.tar.gz diff --git a/engine/src/flutter/third_party/markupsafe/OWNERS b/engine/src/flutter/third_party/markupsafe/OWNERS new file mode 100644 index 0000000000..8edbdf893c --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/OWNERS @@ -0,0 +1,3 @@ +timloh@chromium.org +haraken@chromium.org +nbarth@chromium.org diff --git a/engine/src/flutter/third_party/markupsafe/README.chromium b/engine/src/flutter/third_party/markupsafe/README.chromium new file mode 100644 index 0000000000..0fcab52fa2 --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/README.chromium @@ -0,0 +1,24 @@ +Name: MarkupSafe Python Safe String Class +Short Name: markupsafe +URL: https://github.com/mitsuhiko/markupsafe +Version: 0.18 +License: BSD 3-clause License +License File: NOT_SHIPPED +Security Critical: no + +Description: +Safe string class, used by Jinja2 template engine. + +Source: +https://pypi.python.org/packages/source/M/MarkupSafe/MarkupSafe-0.18.tar.gz +MD5: f8d252fd05371e51dec2fe9a36890687 +SHA-512: 0438ddf0fdab465c40d9afba8c14ad346be0868df654c11130d05e329992d456 + a9bc278551970cbd09244a29c77213885d0c363c951b0cfd4d9aa95b248ecff5 + +Local Modifications: +This only includes the markup directory from the tarball and the LICENSE and +AUTHORS files, removing the unneeded unit tests (tests.py). +Also includes install script (get_markupsafe.sh) and files of hashes (MD5 is +also posted on website, SHA-512 computed locally); script checks hash then +unpacks archive and installs desired files. +Retrieve or update by executing markupsafe/get_markupsafe.sh from third_party. diff --git a/engine/src/flutter/third_party/markupsafe/__init__.py b/engine/src/flutter/third_party/markupsafe/__init__.py new file mode 100644 index 0000000000..25f00d3a4f --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/__init__.py @@ -0,0 +1,234 @@ +# -*- coding: utf-8 -*- +""" + markupsafe + ~~~~~~~~~~ + + Implements a Markup string. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import re +from markupsafe._compat import text_type, string_types, int_types, \ + unichr, PY2 + + +__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent'] + + +_striptags_re = re.compile(r'(|<[^>]*>)') +_entity_re = re.compile(r'&([^;]+);') + + +class Markup(text_type): + r"""Marks a string as being safe for inclusion in HTML/XML output without + needing to be escaped. This implements the `__html__` interface a couple + of frameworks and web applications use. :class:`Markup` is a direct + subclass of `unicode` and provides all the methods of `unicode` just that + it escapes arguments passed and always returns `Markup`. + + The `escape` function returns markup objects so that double escaping can't + happen. + + The constructor of the :class:`Markup` class can be used for three + different things: When passed an unicode object it's assumed to be safe, + when passed an object with an HTML representation (has an `__html__` + method) that representation is used, otherwise the object passed is + converted into a unicode string and then assumed to be safe: + + >>> Markup("Hello World!") + Markup(u'Hello World!') + >>> class Foo(object): + ... def __html__(self): + ... return 'foo' + ... + >>> Markup(Foo()) + Markup(u'foo') + + If you want object passed being always treated as unsafe you can use the + :meth:`escape` classmethod to create a :class:`Markup` object: + + >>> Markup.escape("Hello World!") + Markup(u'Hello <em>World</em>!') + + Operations on a markup string are markup aware which means that all + arguments are passed through the :func:`escape` function: + + >>> em = Markup("%s") + >>> em % "foo & bar" + Markup(u'foo & bar') + >>> strong = Markup("%(text)s") + >>> strong % {'text': 'hacker here'} + Markup(u'<blink>hacker here</blink>') + >>> Markup("Hello ") + "" + Markup(u'Hello <foo>') + """ + __slots__ = () + + def __new__(cls, base=u'', encoding=None, errors='strict'): + if hasattr(base, '__html__'): + base = base.__html__() + if encoding is None: + return text_type.__new__(cls, base) + return text_type.__new__(cls, base, encoding, errors) + + def __html__(self): + return self + + def __add__(self, other): + if isinstance(other, string_types) or hasattr(other, '__html__'): + return self.__class__(super(Markup, self).__add__(self.escape(other))) + return NotImplemented + + def __radd__(self, other): + if hasattr(other, '__html__') or isinstance(other, string_types): + return self.escape(other).__add__(self) + return NotImplemented + + def __mul__(self, num): + if isinstance(num, int_types): + return self.__class__(text_type.__mul__(self, num)) + return NotImplemented + __rmul__ = __mul__ + + def __mod__(self, arg): + if isinstance(arg, tuple): + arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg) + else: + arg = _MarkupEscapeHelper(arg, self.escape) + return self.__class__(text_type.__mod__(self, arg)) + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + text_type.__repr__(self) + ) + + def join(self, seq): + return self.__class__(text_type.join(self, map(self.escape, seq))) + join.__doc__ = text_type.join.__doc__ + + def split(self, *args, **kwargs): + return list(map(self.__class__, text_type.split(self, *args, **kwargs))) + split.__doc__ = text_type.split.__doc__ + + def rsplit(self, *args, **kwargs): + return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs))) + rsplit.__doc__ = text_type.rsplit.__doc__ + + def splitlines(self, *args, **kwargs): + return list(map(self.__class__, text_type.splitlines(self, *args, **kwargs))) + splitlines.__doc__ = text_type.splitlines.__doc__ + + def unescape(self): + r"""Unescape markup again into an text_type string. This also resolves + known HTML4 and XHTML entities: + + >>> Markup("Main » About").unescape() + u'Main \xbb About' + """ + from markupsafe._constants import HTML_ENTITIES + def handle_match(m): + name = m.group(1) + if name in HTML_ENTITIES: + return unichr(HTML_ENTITIES[name]) + try: + if name[:2] in ('#x', '#X'): + return unichr(int(name[2:], 16)) + elif name.startswith('#'): + return unichr(int(name[1:])) + except ValueError: + pass + return u'' + return _entity_re.sub(handle_match, text_type(self)) + + def striptags(self): + r"""Unescape markup into an text_type string and strip all tags. This + also resolves known HTML4 and XHTML entities. Whitespace is + normalized to one: + + >>> Markup("Main » About").striptags() + u'Main \xbb About' + """ + stripped = u' '.join(_striptags_re.sub('', self).split()) + return Markup(stripped).unescape() + + @classmethod + def escape(cls, s): + """Escape the string. Works like :func:`escape` with the difference + that for subclasses of :class:`Markup` this function would return the + correct subclass. + """ + rv = escape(s) + if rv.__class__ is not cls: + return cls(rv) + return rv + + def make_wrapper(name): + orig = getattr(text_type, name) + def func(self, *args, **kwargs): + args = _escape_argspec(list(args), enumerate(args), self.escape) + #_escape_argspec(kwargs, kwargs.iteritems(), None) + return self.__class__(orig(self, *args, **kwargs)) + func.__name__ = orig.__name__ + func.__doc__ = orig.__doc__ + return func + + for method in '__getitem__', 'capitalize', \ + 'title', 'lower', 'upper', 'replace', 'ljust', \ + 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \ + 'translate', 'expandtabs', 'swapcase', 'zfill': + locals()[method] = make_wrapper(method) + + # new in python 2.5 + if hasattr(text_type, 'partition'): + def partition(self, sep): + return tuple(map(self.__class__, + text_type.partition(self, self.escape(sep)))) + def rpartition(self, sep): + return tuple(map(self.__class__, + text_type.rpartition(self, self.escape(sep)))) + + # new in python 2.6 + if hasattr(text_type, 'format'): + format = make_wrapper('format') + + # not in python 3 + if hasattr(text_type, '__getslice__'): + __getslice__ = make_wrapper('__getslice__') + + del method, make_wrapper + + +def _escape_argspec(obj, iterable, escape): + """Helper for various string-wrapped functions.""" + for key, value in iterable: + if hasattr(value, '__html__') or isinstance(value, string_types): + obj[key] = escape(value) + return obj + + +class _MarkupEscapeHelper(object): + """Helper for Markup.__mod__""" + + def __init__(self, obj, escape): + self.obj = obj + self.escape = escape + + __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape) + __unicode__ = __str__ = lambda s: text_type(s.escape(s.obj)) + __repr__ = lambda s: str(s.escape(repr(s.obj))) + __int__ = lambda s: int(s.obj) + __float__ = lambda s: float(s.obj) + + +# we have to import it down here as the speedups and native +# modules imports the markup type which is define above. +try: + from markupsafe._speedups import escape, escape_silent, soft_unicode +except ImportError: + from markupsafe._native import escape, escape_silent, soft_unicode + +if not PY2: + soft_str = soft_unicode + __all__.append('soft_str') diff --git a/engine/src/flutter/third_party/markupsafe/_compat.py b/engine/src/flutter/third_party/markupsafe/_compat.py new file mode 100644 index 0000000000..29e4a3dac1 --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/_compat.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +""" + markupsafe._compat + ~~~~~~~~~~~~~~~~~~ + + Compatibility module for different Python versions. + + :copyright: (c) 2013 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import sys + +PY2 = sys.version_info[0] == 2 + +if not PY2: + text_type = str + string_types = (str,) + unichr = chr + int_types = (int,) +else: + text_type = unicode + string_types = (str, unicode) + unichr = unichr + int_types = (int, long) diff --git a/engine/src/flutter/third_party/markupsafe/_constants.py b/engine/src/flutter/third_party/markupsafe/_constants.py new file mode 100644 index 0000000000..919bf03c50 --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/_constants.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 -*- +""" + markupsafe._constants + ~~~~~~~~~~~~~~~~~~~~~ + + Highlevel implementation of the Markup string. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + + +HTML_ENTITIES = { + 'AElig': 198, + 'Aacute': 193, + 'Acirc': 194, + 'Agrave': 192, + 'Alpha': 913, + 'Aring': 197, + 'Atilde': 195, + 'Auml': 196, + 'Beta': 914, + 'Ccedil': 199, + 'Chi': 935, + 'Dagger': 8225, + 'Delta': 916, + 'ETH': 208, + 'Eacute': 201, + 'Ecirc': 202, + 'Egrave': 200, + 'Epsilon': 917, + 'Eta': 919, + 'Euml': 203, + 'Gamma': 915, + 'Iacute': 205, + 'Icirc': 206, + 'Igrave': 204, + 'Iota': 921, + 'Iuml': 207, + 'Kappa': 922, + 'Lambda': 923, + 'Mu': 924, + 'Ntilde': 209, + 'Nu': 925, + 'OElig': 338, + 'Oacute': 211, + 'Ocirc': 212, + 'Ograve': 210, + 'Omega': 937, + 'Omicron': 927, + 'Oslash': 216, + 'Otilde': 213, + 'Ouml': 214, + 'Phi': 934, + 'Pi': 928, + 'Prime': 8243, + 'Psi': 936, + 'Rho': 929, + 'Scaron': 352, + 'Sigma': 931, + 'THORN': 222, + 'Tau': 932, + 'Theta': 920, + 'Uacute': 218, + 'Ucirc': 219, + 'Ugrave': 217, + 'Upsilon': 933, + 'Uuml': 220, + 'Xi': 926, + 'Yacute': 221, + 'Yuml': 376, + 'Zeta': 918, + 'aacute': 225, + 'acirc': 226, + 'acute': 180, + 'aelig': 230, + 'agrave': 224, + 'alefsym': 8501, + 'alpha': 945, + 'amp': 38, + 'and': 8743, + 'ang': 8736, + 'apos': 39, + 'aring': 229, + 'asymp': 8776, + 'atilde': 227, + 'auml': 228, + 'bdquo': 8222, + 'beta': 946, + 'brvbar': 166, + 'bull': 8226, + 'cap': 8745, + 'ccedil': 231, + 'cedil': 184, + 'cent': 162, + 'chi': 967, + 'circ': 710, + 'clubs': 9827, + 'cong': 8773, + 'copy': 169, + 'crarr': 8629, + 'cup': 8746, + 'curren': 164, + 'dArr': 8659, + 'dagger': 8224, + 'darr': 8595, + 'deg': 176, + 'delta': 948, + 'diams': 9830, + 'divide': 247, + 'eacute': 233, + 'ecirc': 234, + 'egrave': 232, + 'empty': 8709, + 'emsp': 8195, + 'ensp': 8194, + 'epsilon': 949, + 'equiv': 8801, + 'eta': 951, + 'eth': 240, + 'euml': 235, + 'euro': 8364, + 'exist': 8707, + 'fnof': 402, + 'forall': 8704, + 'frac12': 189, + 'frac14': 188, + 'frac34': 190, + 'frasl': 8260, + 'gamma': 947, + 'ge': 8805, + 'gt': 62, + 'hArr': 8660, + 'harr': 8596, + 'hearts': 9829, + 'hellip': 8230, + 'iacute': 237, + 'icirc': 238, + 'iexcl': 161, + 'igrave': 236, + 'image': 8465, + 'infin': 8734, + 'int': 8747, + 'iota': 953, + 'iquest': 191, + 'isin': 8712, + 'iuml': 239, + 'kappa': 954, + 'lArr': 8656, + 'lambda': 955, + 'lang': 9001, + 'laquo': 171, + 'larr': 8592, + 'lceil': 8968, + 'ldquo': 8220, + 'le': 8804, + 'lfloor': 8970, + 'lowast': 8727, + 'loz': 9674, + 'lrm': 8206, + 'lsaquo': 8249, + 'lsquo': 8216, + 'lt': 60, + 'macr': 175, + 'mdash': 8212, + 'micro': 181, + 'middot': 183, + 'minus': 8722, + 'mu': 956, + 'nabla': 8711, + 'nbsp': 160, + 'ndash': 8211, + 'ne': 8800, + 'ni': 8715, + 'not': 172, + 'notin': 8713, + 'nsub': 8836, + 'ntilde': 241, + 'nu': 957, + 'oacute': 243, + 'ocirc': 244, + 'oelig': 339, + 'ograve': 242, + 'oline': 8254, + 'omega': 969, + 'omicron': 959, + 'oplus': 8853, + 'or': 8744, + 'ordf': 170, + 'ordm': 186, + 'oslash': 248, + 'otilde': 245, + 'otimes': 8855, + 'ouml': 246, + 'para': 182, + 'part': 8706, + 'permil': 8240, + 'perp': 8869, + 'phi': 966, + 'pi': 960, + 'piv': 982, + 'plusmn': 177, + 'pound': 163, + 'prime': 8242, + 'prod': 8719, + 'prop': 8733, + 'psi': 968, + 'quot': 34, + 'rArr': 8658, + 'radic': 8730, + 'rang': 9002, + 'raquo': 187, + 'rarr': 8594, + 'rceil': 8969, + 'rdquo': 8221, + 'real': 8476, + 'reg': 174, + 'rfloor': 8971, + 'rho': 961, + 'rlm': 8207, + 'rsaquo': 8250, + 'rsquo': 8217, + 'sbquo': 8218, + 'scaron': 353, + 'sdot': 8901, + 'sect': 167, + 'shy': 173, + 'sigma': 963, + 'sigmaf': 962, + 'sim': 8764, + 'spades': 9824, + 'sub': 8834, + 'sube': 8838, + 'sum': 8721, + 'sup': 8835, + 'sup1': 185, + 'sup2': 178, + 'sup3': 179, + 'supe': 8839, + 'szlig': 223, + 'tau': 964, + 'there4': 8756, + 'theta': 952, + 'thetasym': 977, + 'thinsp': 8201, + 'thorn': 254, + 'tilde': 732, + 'times': 215, + 'trade': 8482, + 'uArr': 8657, + 'uacute': 250, + 'uarr': 8593, + 'ucirc': 251, + 'ugrave': 249, + 'uml': 168, + 'upsih': 978, + 'upsilon': 965, + 'uuml': 252, + 'weierp': 8472, + 'xi': 958, + 'yacute': 253, + 'yen': 165, + 'yuml': 255, + 'zeta': 950, + 'zwj': 8205, + 'zwnj': 8204 +} diff --git a/engine/src/flutter/third_party/markupsafe/_native.py b/engine/src/flutter/third_party/markupsafe/_native.py new file mode 100644 index 0000000000..5e83f10a11 --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/_native.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +""" + markupsafe._native + ~~~~~~~~~~~~~~~~~~ + + Native Python implementation the C module is not compiled. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +from markupsafe import Markup +from markupsafe._compat import text_type + + +def escape(s): + """Convert the characters &, <, >, ' and " in string s to HTML-safe + sequences. Use this if you need to display text that might contain + such characters in HTML. Marks return value as markup string. + """ + if hasattr(s, '__html__'): + return s.__html__() + return Markup(text_type(s) + .replace('&', '&') + .replace('>', '>') + .replace('<', '<') + .replace("'", ''') + .replace('"', '"') + ) + + +def escape_silent(s): + """Like :func:`escape` but converts `None` into an empty + markup string. + """ + if s is None: + return Markup() + return escape(s) + + +def soft_unicode(s): + """Make a string unicode if it isn't already. That way a markup + string is not converted back to unicode. + """ + if not isinstance(s, text_type): + s = text_type(s) + return s diff --git a/engine/src/flutter/third_party/markupsafe/_speedups.c b/engine/src/flutter/third_party/markupsafe/_speedups.c new file mode 100644 index 0000000000..f349febf22 --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/_speedups.c @@ -0,0 +1,239 @@ +/** + * markupsafe._speedups + * ~~~~~~~~~~~~~~~~~~~~ + * + * This module implements functions for automatic escaping in C for better + * performance. + * + * :copyright: (c) 2010 by Armin Ronacher. + * :license: BSD. + */ + +#include + +#define ESCAPED_CHARS_TABLE_SIZE 63 +#define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL))); + +#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) +typedef int Py_ssize_t; +#define PY_SSIZE_T_MAX INT_MAX +#define PY_SSIZE_T_MIN INT_MIN +#endif + + +static PyObject* markup; +static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE]; +static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE]; + +static int +init_constants(void) +{ + PyObject *module; + /* happing of characters to replace */ + escaped_chars_repl['"'] = UNICHR("""); + escaped_chars_repl['\''] = UNICHR("'"); + escaped_chars_repl['&'] = UNICHR("&"); + escaped_chars_repl['<'] = UNICHR("<"); + escaped_chars_repl['>'] = UNICHR(">"); + + /* lengths of those characters when replaced - 1 */ + memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len)); + escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \ + escaped_chars_delta_len['&'] = 4; + escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3; + + /* import markup type so that we can mark the return value */ + module = PyImport_ImportModule("markupsafe"); + if (!module) + return 0; + markup = PyObject_GetAttrString(module, "Markup"); + Py_DECREF(module); + + return 1; +} + +static PyObject* +escape_unicode(PyUnicodeObject *in) +{ + PyUnicodeObject *out; + Py_UNICODE *inp = PyUnicode_AS_UNICODE(in); + const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in); + Py_UNICODE *next_escp; + Py_UNICODE *outp; + Py_ssize_t delta=0, erepl=0, delta_len=0; + + /* First we need to figure out how long the escaped string will be */ + while (*(inp) || inp < inp_end) { + if (*inp < ESCAPED_CHARS_TABLE_SIZE) { + delta += escaped_chars_delta_len[*inp]; + erepl += !!escaped_chars_delta_len[*inp]; + } + ++inp; + } + + /* Do we need to escape anything at all? */ + if (!erepl) { + Py_INCREF(in); + return (PyObject*)in; + } + + out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta); + if (!out) + return NULL; + + outp = PyUnicode_AS_UNICODE(out); + inp = PyUnicode_AS_UNICODE(in); + while (erepl-- > 0) { + /* look for the next substitution */ + next_escp = inp; + while (next_escp < inp_end) { + if (*next_escp < ESCAPED_CHARS_TABLE_SIZE && + (delta_len = escaped_chars_delta_len[*next_escp])) { + ++delta_len; + break; + } + ++next_escp; + } + + if (next_escp > inp) { + /* copy unescaped chars between inp and next_escp */ + Py_UNICODE_COPY(outp, inp, next_escp-inp); + outp += next_escp - inp; + } + + /* escape 'next_escp' */ + Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len); + outp += delta_len; + + inp = next_escp + 1; + } + if (inp < inp_end) + Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in))); + + return (PyObject*)out; +} + + +static PyObject* +escape(PyObject *self, PyObject *text) +{ + PyObject *s = NULL, *rv = NULL, *html; + + /* we don't have to escape integers, bools or floats */ + if (PyLong_CheckExact(text) || +#if PY_MAJOR_VERSION < 3 + PyInt_CheckExact(text) || +#endif + PyFloat_CheckExact(text) || PyBool_Check(text) || + text == Py_None) + return PyObject_CallFunctionObjArgs(markup, text, NULL); + + /* if the object has an __html__ method that performs the escaping */ + html = PyObject_GetAttrString(text, "__html__"); + if (html) { + rv = PyObject_CallObject(html, NULL); + Py_DECREF(html); + return rv; + } + + /* otherwise make the object unicode if it isn't, then escape */ + PyErr_Clear(); + if (!PyUnicode_Check(text)) { +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyObject_Unicode(text); +#else + PyObject *unicode = PyObject_Str(text); +#endif + if (!unicode) + return NULL; + s = escape_unicode((PyUnicodeObject*)unicode); + Py_DECREF(unicode); + } + else + s = escape_unicode((PyUnicodeObject*)text); + + /* convert the unicode string into a markup object. */ + rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL); + Py_DECREF(s); + return rv; +} + + +static PyObject* +escape_silent(PyObject *self, PyObject *text) +{ + if (text != Py_None) + return escape(self, text); + return PyObject_CallFunctionObjArgs(markup, NULL); +} + + +static PyObject* +soft_unicode(PyObject *self, PyObject *s) +{ + if (!PyUnicode_Check(s)) +#if PY_MAJOR_VERSION < 3 + return PyObject_Unicode(s); +#else + return PyObject_Str(s); +#endif + Py_INCREF(s); + return s; +} + + +static PyMethodDef module_methods[] = { + {"escape", (PyCFunction)escape, METH_O, + "escape(s) -> markup\n\n" + "Convert the characters &, <, >, ', and \" in string s to HTML-safe\n" + "sequences. Use this if you need to display text that might contain\n" + "such characters in HTML. Marks return value as markup string."}, + {"escape_silent", (PyCFunction)escape_silent, METH_O, + "escape_silent(s) -> markup\n\n" + "Like escape but converts None to an empty string."}, + {"soft_unicode", (PyCFunction)soft_unicode, METH_O, + "soft_unicode(object) -> string\n\n" + "Make a string unicode if it isn't already. That way a markup\n" + "string is not converted back to unicode."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + + +#if PY_MAJOR_VERSION < 3 + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +init_speedups(void) +{ + if (!init_constants()) + return; + + Py_InitModule3("markupsafe._speedups", module_methods, ""); +} + +#else /* Python 3.x module initialization */ + +static struct PyModuleDef module_definition = { + PyModuleDef_HEAD_INIT, + "markupsafe._speedups", + NULL, + -1, + module_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__speedups(void) +{ + if (!init_constants()) + return NULL; + + return PyModule_Create(&module_definition); +} + +#endif diff --git a/engine/src/flutter/third_party/markupsafe/get_markupsafe.sh b/engine/src/flutter/third_party/markupsafe/get_markupsafe.sh new file mode 100755 index 0000000000..d268832df8 --- /dev/null +++ b/engine/src/flutter/third_party/markupsafe/get_markupsafe.sh @@ -0,0 +1,121 @@ +#!/bin/bash +# Download and extract MarkupSafe +# Homepage: +# https://github.com/mitsuhiko/markupsafe +# Download page: +# https://pypi.python.org/pypi/MarkupSafe +PACKAGE='MarkupSafe' +VERSION='0.18' +PACKAGE_DIR='markupsafe' + +CHROMIUM_FILES="README.chromium OWNERS get_markupsafe.sh" +EXTRA_FILES='LICENSE AUTHORS' +REMOVE_FILES='tests.py' + +SRC_URL='https://pypi.python.org/packages/source/' +SRC_URL+="${PACKAGE:0:1}/$PACKAGE/$PACKAGE-$VERSION.tar.gz" +FILENAME="$(basename $SRC_URL)" +MD5_FILENAME="$FILENAME.md5" +SHA512_FILENAME="$FILENAME.sha512" +CHROMIUM_FILES+=" $MD5_FILENAME $SHA512_FILENAME" + +BUILD_DIR="$PACKAGE-$VERSION" +THIRD_PARTY="$(dirname $(realpath $(dirname "${BASH_SOURCE[0]}")))" +INSTALL_DIR="$THIRD_PARTY/$PACKAGE_DIR" +OUT_DIR="$INSTALL_DIR/$BUILD_DIR/$PACKAGE_DIR" +OLD_DIR="$THIRD_PARTY/$PACKAGE_DIR.old" + +function check_hashes { + # Hashes generated via: + # FILENAME=MarkupSafe-0.18.tar.gz + # md5sum "$FILENAME" > "$FILENAME.md5" + # sha512sum "$FILENAME" > "$FILENAME.sha512" + # unset FILENAME + + # MD5 + if ! [ -f "$MD5_FILENAME" ] + then + echo "MD5 hash file $MD5_FILENAME not found, could not verify archive" + exit 1 + fi + + # 32-digit hash, followed by filename + MD5_HASHFILE_REGEX="^[0-9a-f]{32} $FILENAME" + if ! grep --extended-regex --line-regex --silent \ + "$MD5_HASHFILE_REGEX" "$MD5_FILENAME" + then + echo "MD5 hash file $MD5_FILENAME does not contain hash for $FILENAME," \ + 'could not verify archive' + echo 'Hash file contents are:' + cat "$MD5_FILENAME" + exit 1 + fi + + if ! md5sum --check "$MD5_FILENAME" + then + echo 'MD5 hash does not match,' \ + "archive file $FILENAME corrupt or compromised!" + exit 1 + fi + + # SHA-512 + if ! [ -f "$SHA512_FILENAME" ] + then + echo "SHA-512 hash file $SHA512_FILENAME not found," \ + 'could not verify archive' + exit 1 + fi + + # 128-digit hash, followed by filename + SHA512_HASHFILE_REGEX="^[0-9a-f]{128} $FILENAME" + if ! grep --extended-regex --line-regex --silent \ + "$SHA512_HASHFILE_REGEX" "$SHA512_FILENAME" + then + echo "SHA-512 hash file $SHA512_FILENAME does not contain hash for" \ + "$FILENAME, could not verify archive" + echo 'Hash file contents are:' + cat "$SHA512_FILENAME" + exit 1 + fi + + if ! sha512sum --check "$SHA512_FILENAME" + then + echo 'SHA-512 hash does not match,' \ + "archive file $FILENAME corrupt or compromised!" + exit 1 + fi +} + + +################################################################################ +# Body + +cd "$INSTALL_DIR" +echo "Downloading $SRC_URL" +curl --remote-name "$SRC_URL" +check_hashes +tar xvzf "$FILENAME" +# Copy extra files over +for FILE in $CHROMIUM_FILES +do + cp "$FILE" "$OUT_DIR" +done + +cd "$BUILD_DIR" +for FILE in $EXTRA_FILES +do + cp "$FILE" "$OUT_DIR" +done + +cd "$OUT_DIR" +for FILE in $REMOVE_FILES +do + rm -fr "$FILE" +done + +# Replace with new directory +cd .. +mv "$INSTALL_DIR" "$OLD_DIR" +mv "$PACKAGE_DIR" "$INSTALL_DIR" +cd "$INSTALL_DIR" +rm -fr "$OLD_DIR" diff --git a/engine/src/flutter/third_party/mesa/BUILD.gn b/engine/src/flutter/third_party/mesa/BUILD.gn new file mode 100644 index 0000000000..acdca86162 --- /dev/null +++ b/engine/src/flutter/third_party/mesa/BUILD.gn @@ -0,0 +1,696 @@ +# Copyright 2014 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. + +import("//build/config/ui.gni") + +config("mesa_headers_config") { + include_dirs = [ "src/include" ] + if (use_x11) { + defines = [ "MESA_EGL_NO_X11_HEADERS" ] + } +} + +# This directory contains checked-in files generated from the Mesa build. +generated_src_dir = "src/chromium_gensrc" + +source_set("mesa_headers") { + public_configs = [ ":mesa_headers_config" ] +} + +# This config must generally be prepended to the configs list so that the Mesa +# dirs appear before the system ones on Windows. This is necessary so that +# #include "GL/gl.h" means to include Mesa's, not the system's. +config("mesa_internal_config") { + cflags = [] + defines = [ + "MAPI_ABI_HEADER=\"glapi_mapi_tmp_shared.h\"", + "PACKAGE_NAME=\"Mesa\"", + "PACKAGE_TARNAME=\"mesa\"", + "PACKAGE_VERSION=\"9.0.3\"", + "PACKAGE_STRING=\"Mesa\ 9.0.3\"", + "PACKAGE_BUGREPORT=\"https://bugs.freedesktop.org/enter_bug.cgi\?product=Mesa\"", + "PACKAGE_URL=\"\"", + "PACKAGE=\"mesa\"", + "VERSION=\"9.0.3\"", + "STDC_HEADERS=1", + "HAVE_SYS_TYPES_H=1", + "HAVE_SYS_STAT_H=1", + "HAVE_STDLIB_H=1", + "HAVE_STRING_H=1", + "HAVE_MEMORY_H=1", + "HAVE_STRINGS_H=1", + "HAVE_INTTYPES_H=1", + "HAVE_STDINT_H=1", + "HAVE_DLFCN_H=1", + "LT_OBJDIR=\".libs/\"", + "YYTEXT_POINTER=1", + "HAVE_LIBEXPAT=1", + "HAVE_LIBXCB_DRI2=1", + "FEATURE_GL=1", + "MAPI_MODE_GLAPI", + + #"USE_X86_64_ASM", + "IN_DRI_DRIVER", + "USE_XCB", + "GLX_INDIRECT_RENDERING", + "GLX_DIRECT_RENDERING", + "USE_EXTERNAL_DXTN_LIB=1", + "IN_DRI_DRIVER", + "HAVE_ALIAS", + "HAVE_MINCORE", + "HAVE_LIBUDEV", + "_GLAPI_NO_EXPORTS", + ] + include_dirs = [ + "src/src/gallium/auxiliary", + "src/src/gallium/include", + "src/src/glsl", + "src/src/glsl/glcpp", + "src/src/mapi", + "src/src/mapi/glapi", + "src/src/mesa", + "src/src/mesa/main", + "src/include", + "$generated_src_dir/mesa/", + "$generated_src_dir/mesa/main", + "$generated_src_dir/mesa/program", + "$generated_src_dir/mesa/glapi", + ] + + if (is_clang) { + cflags += [ "-Wno-tautological-constant-out-of-range-compare" ] + } + + if (is_android) { + defines += [ + "__GLIBC__", + "_GNU_SOURCE", + ] + } + + if (is_linux) { + defines += [ "_GNU_SOURCE" ] + } + + if (is_posix) { + defines += [ + "HAVE_DLOPEN", + "HAVE_PTHREAD=1", + "HAVE_UNISTD_H=1", + ] + + if (!is_android) { + defines += [ "HAVE_POSIX_MEMALIGN" ] + } + + if (!is_android && !is_mac) { + cflags += [ "-fPIC" ] + } + } + + if (is_win) { + # TODO(scottmg): http://crbug.com/143877 These should be removed if + # Mesa is ever rolled and the warnings are fixed. + cflags += [ + "/wd4005", # Macro defined twice. + "/wd4065", # Switch statement contains 'default' but no 'case' labels. + "/wd4090", # 'Operation' : different 'modifier' qualifiers + "/wd4099", # Type name struct-vs-class doesn't match. + "/wd4267", # size_t to type. + "/wd4273", # Inconsistent DLL linkage. + "/wd4291", # No matching operator delete found for placement new. + "/wd4305", # Truncation from int to float. + "/wd4334", # Result of 32-bit shift implicitly converted to 64 bits. + "/wd4345", # POD-type default initializers. + ] + } +} + +static_library("mesa_libglslcommon") { + sources = [ + "$generated_src_dir/mesa/glcpp-lex.c", + "$generated_src_dir/mesa/glcpp-parse.c", + "$generated_src_dir/mesa/glcpp-parse.h", + "$generated_src_dir/mesa/glsl_lexer.cc", + "$generated_src_dir/mesa/glsl_parser.cc", + "$generated_src_dir/mesa/main/dispatch.h", + "src/src/glsl/ast_expr.cpp", + "src/src/glsl/ast_function.cpp", + "src/src/glsl/ast_to_hir.cpp", + "src/src/glsl/ast_type.cpp", + "src/src/glsl/builtin_variables.cpp", + "src/src/glsl/glcpp/glcpp.h", + "src/src/glsl/glcpp/pp.c", + "src/src/glsl/glsl_parser_extras.cpp", + "src/src/glsl/glsl_parser_extras.h", + "src/src/glsl/glsl_symbol_table.cpp", + "src/src/glsl/glsl_symbol_table.h", + "src/src/glsl/glsl_types.cpp", + "src/src/glsl/glsl_types.h", + "src/src/glsl/hir_field_selection.cpp", + "src/src/glsl/ir.cpp", + "src/src/glsl/ir.h", + "src/src/glsl/ir_basic_block.cpp", + "src/src/glsl/ir_basic_block.h", + "src/src/glsl/ir_builder.cpp", + "src/src/glsl/ir_builder.h", + "src/src/glsl/ir_clone.cpp", + "src/src/glsl/ir_constant_expression.cpp", + "src/src/glsl/ir_expression_flattening.cpp", + "src/src/glsl/ir_expression_flattening.h", + "src/src/glsl/ir_function.cpp", + "src/src/glsl/ir_function_can_inline.cpp", + "src/src/glsl/ir_function_detect_recursion.cpp", + "src/src/glsl/ir_hierarchical_visitor.cpp", + "src/src/glsl/ir_hierarchical_visitor.h", + "src/src/glsl/ir_hv_accept.cpp", + "src/src/glsl/ir_import_prototypes.cpp", + "src/src/glsl/ir_print_visitor.cpp", + "src/src/glsl/ir_print_visitor.h", + "src/src/glsl/ir_reader.cpp", + "src/src/glsl/ir_reader.h", + "src/src/glsl/ir_rvalue_visitor.cpp", + "src/src/glsl/ir_rvalue_visitor.h", + "src/src/glsl/ir_set_program_inouts.cpp", + "src/src/glsl/ir_validate.cpp", + "src/src/glsl/ir_variable_refcount.cpp", + "src/src/glsl/ir_variable_refcount.h", + "src/src/glsl/link_functions.cpp", + "src/src/glsl/link_uniform_initializers.cpp", + "src/src/glsl/link_uniforms.cpp", + "src/src/glsl/linker.cpp", + "src/src/glsl/linker.h", + "src/src/glsl/loop_analysis.cpp", + "src/src/glsl/loop_analysis.h", + "src/src/glsl/loop_controls.cpp", + "src/src/glsl/loop_unroll.cpp", + "src/src/glsl/lower_clip_distance.cpp", + "src/src/glsl/lower_discard.cpp", + "src/src/glsl/lower_discard_flow.cpp", + "src/src/glsl/lower_if_to_cond_assign.cpp", + "src/src/glsl/lower_instructions.cpp", + "src/src/glsl/lower_jumps.cpp", + "src/src/glsl/lower_mat_op_to_vec.cpp", + "src/src/glsl/lower_noise.cpp", + "src/src/glsl/lower_output_reads.cpp", + "src/src/glsl/lower_texture_projection.cpp", + "src/src/glsl/lower_ubo_reference.cpp", + "src/src/glsl/lower_variable_index_to_cond_assign.cpp", + "src/src/glsl/lower_vec_index_to_cond_assign.cpp", + "src/src/glsl/lower_vec_index_to_swizzle.cpp", + "src/src/glsl/lower_vector.cpp", + "src/src/glsl/opt_algebraic.cpp", + "src/src/glsl/opt_array_splitting.cpp", + "src/src/glsl/opt_constant_folding.cpp", + "src/src/glsl/opt_constant_propagation.cpp", + "src/src/glsl/opt_constant_variable.cpp", + "src/src/glsl/opt_copy_propagation.cpp", + "src/src/glsl/opt_copy_propagation_elements.cpp", + "src/src/glsl/opt_dead_code.cpp", + "src/src/glsl/opt_dead_code_local.cpp", + "src/src/glsl/opt_dead_functions.cpp", + "src/src/glsl/opt_function_inlining.cpp", + "src/src/glsl/opt_if_simplification.cpp", + "src/src/glsl/opt_noop_swizzle.cpp", + "src/src/glsl/opt_redundant_jumps.cpp", + "src/src/glsl/opt_structure_splitting.cpp", + "src/src/glsl/opt_swizzle_swizzle.cpp", + "src/src/glsl/opt_tree_grafting.cpp", + "src/src/glsl/program.h", + "src/src/glsl/ralloc.c", + "src/src/glsl/ralloc.h", + "src/src/glsl/s_expression.cpp", + "src/src/glsl/s_expression.h", + + # Skipped in the GN build. This file duplicates symbols from errors.c and + # happens to link in GYP due to static library link ordering. + #"src/src/glsl/standalone_scaffolding.cpp", + #"src/src/glsl/standalone_scaffolding.h", + "src/src/glsl/strtod.c", + "src/src/glsl/strtod.h", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + previous_configs = configs + configs = [] + configs = [ ":mesa_internal_config" ] + previous_configs + + if (is_clang) { + # Mesa triggers some of these Clang warnings. + configs -= [ "//build/config/clang:extra_warnings" ] + } + + deps = [ + ":mesa_headers", + ] +} + +static_library("mesa") { + sources = [ + "$generated_src_dir/mesa/builtin_function.cpp", + "$generated_src_dir/mesa/enums.c", + "$generated_src_dir/mesa/glapi_mapi_tmp_shared.h", + "$generated_src_dir/mesa/lex.yy.c", + "$generated_src_dir/mesa/program/program_parse.tab.c", + "$generated_src_dir/mesa/program/program_parse.tab.h", + "src/src/mapi/mapi/entry.c", + "src/src/mapi/mapi/entry.h", + "src/src/mapi/mapi/mapi_glapi.c", + "src/src/mapi/mapi/stub.c", + "src/src/mapi/mapi/stub.h", + "src/src/mapi/mapi/table.c", + "src/src/mapi/mapi/table.h", + "src/src/mapi/mapi/u_current.c", + "src/src/mapi/mapi/u_current.h", + "src/src/mapi/mapi/u_execmem.c", + "src/src/mapi/mapi/u_execmem.h", + "src/src/mesa/main/accum.c", + "src/src/mesa/main/accum.h", + "src/src/mesa/main/api_arrayelt.c", + "src/src/mesa/main/api_arrayelt.h", + "src/src/mesa/main/api_exec.c", + "src/src/mesa/main/api_exec.h", + "src/src/mesa/main/api_loopback.c", + "src/src/mesa/main/api_loopback.h", + "src/src/mesa/main/api_validate.c", + "src/src/mesa/main/api_validate.h", + "src/src/mesa/main/arbprogram.c", + "src/src/mesa/main/arbprogram.h", + "src/src/mesa/main/arrayobj.c", + "src/src/mesa/main/arrayobj.h", + "src/src/mesa/main/atifragshader.c", + "src/src/mesa/main/atifragshader.h", + "src/src/mesa/main/attrib.c", + "src/src/mesa/main/attrib.h", + "src/src/mesa/main/blend.c", + "src/src/mesa/main/blend.h", + "src/src/mesa/main/bufferobj.c", + "src/src/mesa/main/bufferobj.h", + "src/src/mesa/main/buffers.c", + "src/src/mesa/main/buffers.h", + "src/src/mesa/main/clear.c", + "src/src/mesa/main/clear.h", + "src/src/mesa/main/clip.c", + "src/src/mesa/main/clip.h", + "src/src/mesa/main/colortab.c", + "src/src/mesa/main/colortab.h", + "src/src/mesa/main/condrender.c", + "src/src/mesa/main/condrender.h", + "src/src/mesa/main/context.c", + "src/src/mesa/main/context.h", + "src/src/mesa/main/convolve.c", + "src/src/mesa/main/convolve.h", + "src/src/mesa/main/cpuinfo.c", + "src/src/mesa/main/cpuinfo.h", + "src/src/mesa/main/debug.c", + "src/src/mesa/main/debug.h", + "src/src/mesa/main/depth.c", + "src/src/mesa/main/depth.h", + "src/src/mesa/main/dlist.c", + "src/src/mesa/main/dlist.h", + "src/src/mesa/main/drawpix.c", + "src/src/mesa/main/drawpix.h", + "src/src/mesa/main/drawtex.c", + "src/src/mesa/main/drawtex.h", + "src/src/mesa/main/enable.c", + "src/src/mesa/main/enable.h", + "src/src/mesa/main/enums.h", + "src/src/mesa/main/errors.c", + "src/src/mesa/main/errors.h", + "src/src/mesa/main/eval.c", + "src/src/mesa/main/eval.h", + "src/src/mesa/main/execmem.c", + "src/src/mesa/main/extensions.c", + "src/src/mesa/main/extensions.h", + "src/src/mesa/main/fbobject.c", + "src/src/mesa/main/fbobject.h", + "src/src/mesa/main/feedback.c", + "src/src/mesa/main/feedback.h", + "src/src/mesa/main/ff_fragment_shader.cpp", + "src/src/mesa/main/ffvertex_prog.c", + "src/src/mesa/main/ffvertex_prog.h", + "src/src/mesa/main/fog.c", + "src/src/mesa/main/fog.h", + "src/src/mesa/main/format_pack.c", + "src/src/mesa/main/format_pack.h", + "src/src/mesa/main/format_unpack.c", + "src/src/mesa/main/format_unpack.h", + "src/src/mesa/main/formats.c", + "src/src/mesa/main/formats.h", + "src/src/mesa/main/framebuffer.c", + "src/src/mesa/main/framebuffer.h", + "src/src/mesa/main/get.c", + "src/src/mesa/main/get.h", + "src/src/mesa/main/getstring.c", + "src/src/mesa/main/glformats.c", + "src/src/mesa/main/glformats.h", + "src/src/mesa/main/hash.c", + "src/src/mesa/main/hash.h", + "src/src/mesa/main/hint.c", + "src/src/mesa/main/hint.h", + "src/src/mesa/main/histogram.c", + "src/src/mesa/main/histogram.h", + "src/src/mesa/main/image.c", + "src/src/mesa/main/image.h", + "src/src/mesa/main/imports.c", + "src/src/mesa/main/imports.h", + "src/src/mesa/main/light.c", + "src/src/mesa/main/light.h", + "src/src/mesa/main/lines.c", + "src/src/mesa/main/lines.h", + "src/src/mesa/main/matrix.c", + "src/src/mesa/main/matrix.h", + "src/src/mesa/main/mipmap.c", + "src/src/mesa/main/mipmap.h", + "src/src/mesa/main/mm.c", + "src/src/mesa/main/mm.h", + "src/src/mesa/main/multisample.c", + "src/src/mesa/main/multisample.h", + "src/src/mesa/main/nvprogram.c", + "src/src/mesa/main/nvprogram.h", + "src/src/mesa/main/pack.c", + "src/src/mesa/main/pack.h", + "src/src/mesa/main/pbo.c", + "src/src/mesa/main/pbo.h", + "src/src/mesa/main/pixel.c", + "src/src/mesa/main/pixel.h", + "src/src/mesa/main/pixelstore.c", + "src/src/mesa/main/pixelstore.h", + "src/src/mesa/main/pixeltransfer.c", + "src/src/mesa/main/pixeltransfer.h", + "src/src/mesa/main/points.c", + "src/src/mesa/main/points.h", + "src/src/mesa/main/polygon.c", + "src/src/mesa/main/polygon.h", + "src/src/mesa/main/queryobj.c", + "src/src/mesa/main/queryobj.h", + "src/src/mesa/main/rastpos.c", + "src/src/mesa/main/rastpos.h", + "src/src/mesa/main/readpix.c", + "src/src/mesa/main/readpix.h", + "src/src/mesa/main/remap.c", + "src/src/mesa/main/remap.h", + "src/src/mesa/main/renderbuffer.c", + "src/src/mesa/main/renderbuffer.h", + "src/src/mesa/main/samplerobj.c", + "src/src/mesa/main/samplerobj.h", + "src/src/mesa/main/scissor.c", + "src/src/mesa/main/scissor.h", + "src/src/mesa/main/shader_query.cpp", + "src/src/mesa/main/shaderapi.c", + "src/src/mesa/main/shaderapi.h", + "src/src/mesa/main/shaderobj.c", + "src/src/mesa/main/shaderobj.h", + "src/src/mesa/main/shared.c", + "src/src/mesa/main/shared.h", + "src/src/mesa/main/state.c", + "src/src/mesa/main/state.h", + "src/src/mesa/main/stencil.c", + "src/src/mesa/main/stencil.h", + "src/src/mesa/main/syncobj.c", + "src/src/mesa/main/syncobj.h", + "src/src/mesa/main/texcompress.c", + "src/src/mesa/main/texcompress.h", + "src/src/mesa/main/texcompress_cpal.c", + "src/src/mesa/main/texcompress_cpal.h", + "src/src/mesa/main/texcompress_etc.c", + "src/src/mesa/main/texcompress_etc.h", + "src/src/mesa/main/texcompress_fxt1.c", + "src/src/mesa/main/texcompress_fxt1.h", + "src/src/mesa/main/texcompress_rgtc.c", + "src/src/mesa/main/texcompress_rgtc.h", + "src/src/mesa/main/texcompress_s3tc.c", + "src/src/mesa/main/texcompress_s3tc.h", + "src/src/mesa/main/texenv.c", + "src/src/mesa/main/texenv.h", + "src/src/mesa/main/texformat.c", + "src/src/mesa/main/texformat.h", + "src/src/mesa/main/texgen.c", + "src/src/mesa/main/texgen.h", + "src/src/mesa/main/texgetimage.c", + "src/src/mesa/main/texgetimage.h", + "src/src/mesa/main/teximage.c", + "src/src/mesa/main/teximage.h", + "src/src/mesa/main/texobj.c", + "src/src/mesa/main/texobj.h", + "src/src/mesa/main/texparam.c", + "src/src/mesa/main/texparam.h", + "src/src/mesa/main/texstate.c", + "src/src/mesa/main/texstate.h", + "src/src/mesa/main/texstorage.c", + "src/src/mesa/main/texstorage.h", + "src/src/mesa/main/texstore.c", + "src/src/mesa/main/texstore.h", + "src/src/mesa/main/texturebarrier.c", + "src/src/mesa/main/texturebarrier.h", + "src/src/mesa/main/transformfeedback.c", + "src/src/mesa/main/transformfeedback.h", + "src/src/mesa/main/uniform_query.cpp", + "src/src/mesa/main/uniforms.c", + "src/src/mesa/main/uniforms.h", + "src/src/mesa/main/varray.c", + "src/src/mesa/main/varray.h", + "src/src/mesa/main/version.c", + "src/src/mesa/main/version.h", + "src/src/mesa/main/viewport.c", + "src/src/mesa/main/viewport.h", + "src/src/mesa/main/vtxfmt.c", + "src/src/mesa/main/vtxfmt.h", + "src/src/mesa/math/m_debug_clip.c", + "src/src/mesa/math/m_debug_norm.c", + "src/src/mesa/math/m_debug_xform.c", + "src/src/mesa/math/m_eval.c", + "src/src/mesa/math/m_eval.h", + "src/src/mesa/math/m_matrix.c", + "src/src/mesa/math/m_matrix.h", + "src/src/mesa/math/m_translate.c", + "src/src/mesa/math/m_translate.h", + "src/src/mesa/math/m_vector.c", + "src/src/mesa/math/m_vector.h", + "src/src/mesa/math/m_xform.c", + "src/src/mesa/math/m_xform.h", + "src/src/mesa/program/arbprogparse.c", + "src/src/mesa/program/arbprogparse.h", + "src/src/mesa/program/hash_table.c", + "src/src/mesa/program/hash_table.h", + "src/src/mesa/program/ir_to_mesa.cpp", + "src/src/mesa/program/ir_to_mesa.h", + "src/src/mesa/program/nvfragparse.c", + "src/src/mesa/program/nvfragparse.h", + "src/src/mesa/program/nvvertparse.c", + "src/src/mesa/program/nvvertparse.h", + "src/src/mesa/program/prog_cache.c", + "src/src/mesa/program/prog_cache.h", + "src/src/mesa/program/prog_execute.c", + "src/src/mesa/program/prog_execute.h", + "src/src/mesa/program/prog_instruction.c", + "src/src/mesa/program/prog_instruction.h", + "src/src/mesa/program/prog_noise.c", + "src/src/mesa/program/prog_noise.h", + "src/src/mesa/program/prog_opt_constant_fold.c", + "src/src/mesa/program/prog_optimize.c", + "src/src/mesa/program/prog_optimize.h", + "src/src/mesa/program/prog_parameter.c", + "src/src/mesa/program/prog_parameter.h", + "src/src/mesa/program/prog_parameter_layout.c", + "src/src/mesa/program/prog_parameter_layout.h", + "src/src/mesa/program/prog_print.c", + "src/src/mesa/program/prog_print.h", + "src/src/mesa/program/prog_statevars.c", + "src/src/mesa/program/prog_statevars.h", + "src/src/mesa/program/program.c", + "src/src/mesa/program/program.h", + "src/src/mesa/program/program_parse_extra.c", + "src/src/mesa/program/programopt.c", + "src/src/mesa/program/programopt.h", + "src/src/mesa/program/register_allocate.c", + "src/src/mesa/program/register_allocate.h", + "src/src/mesa/program/sampler.cpp", + "src/src/mesa/program/sampler.h", + "src/src/mesa/program/string_to_uint_map.cpp", + "src/src/mesa/program/symbol_table.c", + "src/src/mesa/program/symbol_table.h", + "src/src/mesa/swrast/s_aaline.c", + "src/src/mesa/swrast/s_aaline.h", + "src/src/mesa/swrast/s_aatriangle.c", + "src/src/mesa/swrast/s_aatriangle.h", + "src/src/mesa/swrast/s_alpha.c", + "src/src/mesa/swrast/s_alpha.h", + "src/src/mesa/swrast/s_atifragshader.c", + "src/src/mesa/swrast/s_atifragshader.h", + "src/src/mesa/swrast/s_bitmap.c", + "src/src/mesa/swrast/s_blend.c", + "src/src/mesa/swrast/s_blend.h", + "src/src/mesa/swrast/s_blit.c", + "src/src/mesa/swrast/s_clear.c", + "src/src/mesa/swrast/s_context.c", + "src/src/mesa/swrast/s_context.h", + "src/src/mesa/swrast/s_copypix.c", + "src/src/mesa/swrast/s_depth.c", + "src/src/mesa/swrast/s_depth.h", + "src/src/mesa/swrast/s_drawpix.c", + "src/src/mesa/swrast/s_feedback.c", + "src/src/mesa/swrast/s_feedback.h", + "src/src/mesa/swrast/s_fog.c", + "src/src/mesa/swrast/s_fog.h", + "src/src/mesa/swrast/s_fragprog.c", + "src/src/mesa/swrast/s_fragprog.h", + "src/src/mesa/swrast/s_lines.c", + "src/src/mesa/swrast/s_lines.h", + "src/src/mesa/swrast/s_logic.c", + "src/src/mesa/swrast/s_logic.h", + "src/src/mesa/swrast/s_masking.c", + "src/src/mesa/swrast/s_masking.h", + "src/src/mesa/swrast/s_points.c", + "src/src/mesa/swrast/s_points.h", + "src/src/mesa/swrast/s_renderbuffer.c", + "src/src/mesa/swrast/s_renderbuffer.h", + "src/src/mesa/swrast/s_span.c", + "src/src/mesa/swrast/s_span.h", + "src/src/mesa/swrast/s_stencil.c", + "src/src/mesa/swrast/s_stencil.h", + "src/src/mesa/swrast/s_texcombine.c", + "src/src/mesa/swrast/s_texcombine.h", + "src/src/mesa/swrast/s_texfetch.c", + "src/src/mesa/swrast/s_texfetch.h", + "src/src/mesa/swrast/s_texfilter.c", + "src/src/mesa/swrast/s_texfilter.h", + "src/src/mesa/swrast/s_texrender.c", + "src/src/mesa/swrast/s_texture.c", + "src/src/mesa/swrast/s_triangle.c", + "src/src/mesa/swrast/s_triangle.h", + "src/src/mesa/swrast/s_zoom.c", + "src/src/mesa/swrast/s_zoom.h", + "src/src/mesa/swrast_setup/ss_context.c", + "src/src/mesa/swrast_setup/ss_context.h", + "src/src/mesa/swrast_setup/ss_triangle.c", + "src/src/mesa/swrast_setup/ss_triangle.h", + "src/src/mesa/tnl/t_context.c", + "src/src/mesa/tnl/t_context.h", + "src/src/mesa/tnl/t_draw.c", + "src/src/mesa/tnl/t_pipeline.c", + "src/src/mesa/tnl/t_pipeline.h", + "src/src/mesa/tnl/t_rasterpos.c", + "src/src/mesa/tnl/t_vb_fog.c", + "src/src/mesa/tnl/t_vb_light.c", + "src/src/mesa/tnl/t_vb_normals.c", + "src/src/mesa/tnl/t_vb_points.c", + "src/src/mesa/tnl/t_vb_program.c", + "src/src/mesa/tnl/t_vb_render.c", + "src/src/mesa/tnl/t_vb_texgen.c", + "src/src/mesa/tnl/t_vb_texmat.c", + "src/src/mesa/tnl/t_vb_vertex.c", + "src/src/mesa/tnl/t_vertex.c", + "src/src/mesa/tnl/t_vertex.h", + "src/src/mesa/tnl/t_vertex_generic.c", + "src/src/mesa/tnl/t_vertex_sse.c", + "src/src/mesa/tnl/t_vp_build.c", + "src/src/mesa/tnl/t_vp_build.h", + "src/src/mesa/vbo/vbo_context.c", + "src/src/mesa/vbo/vbo_context.h", + "src/src/mesa/vbo/vbo_exec.c", + "src/src/mesa/vbo/vbo_exec.h", + "src/src/mesa/vbo/vbo_exec_api.c", + "src/src/mesa/vbo/vbo_exec_array.c", + "src/src/mesa/vbo/vbo_exec_draw.c", + "src/src/mesa/vbo/vbo_exec_eval.c", + "src/src/mesa/vbo/vbo_noop.c", + "src/src/mesa/vbo/vbo_noop.h", + "src/src/mesa/vbo/vbo_primitive_restart.c", + "src/src/mesa/vbo/vbo_rebase.c", + "src/src/mesa/vbo/vbo_save.c", + "src/src/mesa/vbo/vbo_save.h", + "src/src/mesa/vbo/vbo_save_api.c", + "src/src/mesa/vbo/vbo_save_draw.c", + "src/src/mesa/vbo/vbo_save_loopback.c", + "src/src/mesa/vbo/vbo_split.c", + "src/src/mesa/vbo/vbo_split.h", + "src/src/mesa/vbo/vbo_split_copy.c", + "src/src/mesa/vbo/vbo_split_inplace.c", + "src/src/mesa/x86-64/x86-64.c", + "src/src/mesa/x86-64/x86-64.h", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + previous_configs = configs + configs = [] + configs = [ ":mesa_internal_config" ] + previous_configs + + if (is_clang) { + # Mesa triggers some of these Clang warnings. + configs -= [ "//build/config/clang:extra_warnings" ] + } + + cflags = [] + + if (is_android && !is_clang) { + # Disable sincos() optimization to avoid a linker error + # since Android's math library doesn't have sincos(). + # Either -fno-builtin-sin or -fno-builtin-cos works. + cflags += [ "-fno-builtin-sin" ] + } + + if (is_win) { + # Because we're building as a static library + defines = [ "_GLAPI_NO_EXPORTS" ] + } + + deps = [ + ":mesa_headers", + ] +} + +if (!is_android) { # TODO(GYP) enable for Android. + # Building this target will hide the native OpenGL shared library and + # replace it with a slow software renderer. + # TODO(GYP) http://crbug.com/380327 need support for loadable_module. + #loadable_module("osmesa") { + shared_library("osmesa") { + sources = [ + "src/src/mesa/drivers/common/driverfuncs.c", + "src/src/mesa/drivers/common/driverfuncs.h", + "src/src/mesa/drivers/common/meta.c", + "src/src/mesa/drivers/common/meta.h", + "src/src/mesa/drivers/osmesa/osmesa.c", + "src/src/mesa/drivers/osmesa/osmesa.def", + ] + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ + ":mesa_headers_config", + "//build/config/compiler:no_chromium_code", + ] + previous_configs = configs + configs = [] + configs = [ ":mesa_internal_config" ] + previous_configs + + include_dirs = [ "src/src/mesa/drivers" ] + + if (is_clang) { + # Mesa triggers some of these Clang warnings. + configs -= [ "//build/config/clang:extra_warnings" ] + } + + deps = [ + ":mesa_headers", + ":mesa", + ":mesa_libglslcommon", + ] + + if (is_win) { + defines = [ + "BUILD_GL32", + "KEYWORD1=GLAPI", + "KEYWORD2=GLAPIENTRY", + ] + } + } +} else { + # Placeholder to allow targets to unconditionally depend on this. + group("osmesa") { + } +} # !is_android +# TODO(GYP) Android osmesa_in_lib_dir target. diff --git a/engine/src/flutter/third_party/mesa/LICENSE b/engine/src/flutter/third_party/mesa/LICENSE new file mode 100644 index 0000000000..792c6fe50e --- /dev/null +++ b/engine/src/flutter/third_party/mesa/LICENSE @@ -0,0 +1,512 @@ +The default Mesa license is as follows: + +Copyright (C) 1999-2007 Brian Paul All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +Some parts of Mesa are copyrighted under the GNU LGPL. See the +Mesa/docs/COPYRIGHT file for details. + +The following is the standard GNU copyright file. +---------------------------------------------------------------------- + + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/engine/src/flutter/third_party/mesa/OWNERS b/engine/src/flutter/third_party/mesa/OWNERS new file mode 100644 index 0000000000..9f0c3e7143 --- /dev/null +++ b/engine/src/flutter/third_party/mesa/OWNERS @@ -0,0 +1,4 @@ +kbr@chromium.org +senorblanco@chromium.org +piman@chromium.org +marcheu@chromium.org diff --git a/engine/src/flutter/third_party/mesa/README.chromium b/engine/src/flutter/third_party/mesa/README.chromium new file mode 100644 index 0000000000..1a1862ff2e --- /dev/null +++ b/engine/src/flutter/third_party/mesa/README.chromium @@ -0,0 +1,59 @@ +Name: mesa +Version: 9.0.3 +URL: http://www.mesa3d.org/ +License: MIT and LGPL v2 +Security Critical: Yes + +Description: +This directory contains a copy of the Mesa sources with minor +modifications to work in Chromium's build infrastructure. + +The license file in this directory is derived from src/docs/license.html +and src/docs/COPYING. + +Modifications made: +- Added the file README.chromium (this file) + +- Disabled optimizations using #pragma optimize('', off) in the + following files: + - src/src/mesa/main/mipmap.c + - src/src/mesa/main/pack.c + - src/src/mesa/math/m_eval.c + - src/src/mesa/swrast/s_texcombine.c + +- Checked in sources normally autogenerated during Mesa's build + process under src/chromium_gensrc. + +- Modified _mesa_add_parameter to not read from uninitialized + memory + +- Added typedefs for EGLNative*Type in eglplatform.h, guarded by an + __APPLE__ define + +- Modified glsl_strtod in src/glsl/strtod.c to use strtod instead of + strtod_l on Android + +- Added an #include for at the top of + src/gallium/auxiliary/util/u_debug.h + +- Fix a bug with Multiple Render Targets, see + https://code.google.com/p/chromium/issues/detail?id=308715 + +- #ifdef out inline definitions of math functions that are present in + VS2013's standard library. + +- #pragma optimize off around _swrast_write_zoomed_z_span, ICEing on + VS2013: http://crbug.com/348350. + +- Disabled "#pragma export" usage in gl.h and osmesa.h, + https://bugs.freedesktop.org/show_bug.cgi?id=77749 + +- Porting to x64 Android. Remove redefinitions of log2 and log2f. + https://codereview.chromium.org/216773005/ + +- Excluded src/mapi/mapi/mapi.{h,c} from the build. + +- Backported f8e7aa2827e2bdb1ed238cbdd351be3c8a6e9b12 and + e20a2df4017ab10dd7199936948c6ac809bfacb6 to fix issues with + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES) when no + framebuffer is bound diff --git a/engine/src/flutter/third_party/mesa/README.txt b/engine/src/flutter/third_party/mesa/README.txt new file mode 100644 index 0000000000..30e4c8cc9c --- /dev/null +++ b/engine/src/flutter/third_party/mesa/README.txt @@ -0,0 +1,15 @@ +Compilation has a few phases: + +1. Generate the header and dispatch source files that have to match the GL api. + These read in a description of the GL api in the form of XML files. In + addition, generate the GLSL parser and lexer using flex and bison. These + sources are needed for step 2 +2. Compile everything in src/glsl into a library. This step uses the parser and + lexer output. +3. Compile the compiler (executable) that can create the builtin functions' + source file. Note that this step uses builtin_stubs.cpp because we haven't + generated the actual builtin functions' source file yet. +4. Invoke the compiler that we just built to create + gen/mesa/builtin_function.cpp +5. Compile the rest of mesa, using the builtins that we created in step 4. In + addition, link in all the files that we've previously compiled in step 2. diff --git a/engine/src/flutter/third_party/mesa/chromium.patch b/engine/src/flutter/third_party/mesa/chromium.patch new file mode 100644 index 0000000000..2d66427579 --- /dev/null +++ b/engine/src/flutter/third_party/mesa/chromium.patch @@ -0,0 +1,2107 @@ +diff -c -r Mesa-7.9/include/GL/gl.h MesaLib/include/GL/gl.h +*** Mesa-7.9/include/GL/gl.h Tue Apr 27 14:41:21 2010 +--- MesaLib/include/GL/gl.h Thu Oct 14 14:41:53 2010 +*************** +*** 55,61 **** + # if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(BUILD_GL32) /* tag specify we're building mesa as a DLL */ + # define GLAPI __declspec(dllexport) + # elif (defined(_MSC_VER) || defined(__MINGW32__)) && defined(_DLL) /* tag specifying we're building for DLL runtime support */ +! # define GLAPI __declspec(dllimport) + # else /* for use with static link lib build of Win32 edition only */ + # define GLAPI extern + # endif /* _STATIC_MESA support */ +--- 55,65 ---- + # if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(BUILD_GL32) /* tag specify we're building mesa as a DLL */ + # define GLAPI __declspec(dllexport) + # elif (defined(_MSC_VER) || defined(__MINGW32__)) && defined(_DLL) /* tag specifying we're building for DLL runtime support */ +! // We always retrieve the entry points dynamically via GetProcAddress or +! // OSMesaGetProcAddress. This works around an issue where using the MSVC +! // multi-threaded runtime library, which defines _DLL. +! //# define GLAPI __declspec(dllimport) +! # define GLAPI + # else /* for use with static link lib build of Win32 edition only */ + # define GLAPI extern + # endif /* _STATIC_MESA support */ +*************** +*** 72,80 **** + # define GLAPIENTRY + #endif /* WIN32 && !CYGWIN */ + +! #if (defined(__BEOS__) && defined(__POWERPC__)) || defined(__QUICKDRAW__) +! # define PRAGMA_EXPORT_SUPPORTED 1 +! #endif + + /* + * WINDOWS: Include windows.h here to define APIENTRY. +--- 76,86 ---- + # define GLAPIENTRY + #endif /* WIN32 && !CYGWIN */ + +! // Disabled this because __QUICKDRAW__ is defined on Mac and gcc does not +! // support the pragma. +! //#if (defined(__BEOS__) && defined(__POWERPC__)) || defined(__QUICKDRAW__) +! //# define PRAGMA_EXPORT_SUPPORTED 1 +! //#endif + + /* + * WINDOWS: Include windows.h here to define APIENTRY. +diff -c -r Mesa-7.9/include/GL/glext.h MesaLib/include/GL/glext.h +*** Mesa-7.9/include/GL/glext.h Fri Oct 1 15:51:28 2010 +--- MesaLib/include/GL/glext.h Thu Oct 14 14:50:37 2010 +*************** +*** 5030,5043 **** + + #ifndef GL_VERSION_1_5 + /* GL types for handling large vertex buffer objects */ +! typedef ptrdiff_t GLintptr; +! typedef ptrdiff_t GLsizeiptr; + #endif + + #ifndef GL_ARB_vertex_buffer_object + /* GL types for handling large vertex buffer objects */ +! typedef ptrdiff_t GLintptrARB; +! typedef ptrdiff_t GLsizeiptrARB; + #endif + + #ifndef GL_ARB_shader_objects +--- 5030,5043 ---- + + #ifndef GL_VERSION_1_5 + /* GL types for handling large vertex buffer objects */ +! typedef signed long int GLintptr; +! typedef signed long int GLsizeiptr; + #endif + + #ifndef GL_ARB_vertex_buffer_object + /* GL types for handling large vertex buffer objects */ +! typedef signed long int GLintptrARB; +! typedef signed long int GLsizeiptrARB; + #endif + + #ifndef GL_ARB_shader_objects +diff -c -r Mesa-7.9/include/GL/osmesa.h MesaLib/include/GL/osmesa.h +*** Mesa-7.9/include/GL/osmesa.h Thu Feb 4 16:10:39 2010 +--- MesaLib/include/GL/osmesa.h Thu Oct 14 14:47:08 2010 +*************** +*** 101,109 **** + typedef struct osmesa_context *OSMesaContext; + + +! #if defined(__BEOS__) || defined(__QUICKDRAW__) +! #pragma export on +! #endif + + + /* +--- 101,111 ---- + typedef struct osmesa_context *OSMesaContext; + + +! // Disabled this because __QUICKDRAW__ is defined on Mac and gcc does not +! // support the pragma. +! //#if defined(__BEOS__) || defined(__QUICKDRAW__) +! //#pragma export on +! //#endif + + + /* +*************** +*** 276,284 **** + OSMesaColorClamp(GLboolean enable); + + +! #if defined(__BEOS__) || defined(__QUICKDRAW__) +! #pragma export off +! #endif + + + #ifdef __cplusplus +--- 278,288 ---- + OSMesaColorClamp(GLboolean enable); + + +! // Disabled this because __QUICKDRAW__ is defined on Mac and gcc does not +! // support the pragma. +! //#if defined(__BEOS__) || defined(__QUICKDRAW__) +! //#pragma export off +! //#endif + + + #ifdef __cplusplus +diff -c -r Mesa-7.9/src/glsl/ast_to_hir.cpp MesaLib/src/glsl/ast_to_hir.cpp +*** Mesa-7.9/src/glsl/ast_to_hir.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/ast_to_hir.cpp Fri Oct 22 15:28:47 2010 +*************** +*** 55,60 **** +--- 55,61 ---- + #include "ast.h" + #include "glsl_types.h" + #include "ir.h" ++ #include "safe_strcmp.h" + + void + _mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) +*************** +*** 1615,1621 **** + var->pixel_center_integer = qual->pixel_center_integer; + var->origin_upper_left = qual->origin_upper_left; + if ((qual->origin_upper_left || qual->pixel_center_integer) +! && (strcmp(var->name, "gl_FragCoord") != 0)) { + const char *const qual_string = (qual->origin_upper_left) + ? "origin_upper_left" : "pixel_center_integer"; + +--- 1616,1622 ---- + var->pixel_center_integer = qual->pixel_center_integer; + var->origin_upper_left = qual->origin_upper_left; + if ((qual->origin_upper_left || qual->pixel_center_integer) +! && (safe_strcmp(var->name, "gl_FragCoord") != 0)) { + const char *const qual_string = (qual->origin_upper_left) + ? "origin_upper_left" : "pixel_center_integer"; + +*************** +*** 2003,2009 **** + * gl_MaxTextureCoords." + */ + const unsigned size = unsigned(var->type->array_size()); +! if ((strcmp("gl_TexCoord", var->name) == 0) + && (size > state->Const.MaxTextureCoords)) { + YYLTYPE loc = this->get_location(); + +--- 2004,2010 ---- + * gl_MaxTextureCoords." + */ + const unsigned size = unsigned(var->type->array_size()); +! if ((safe_strcmp("gl_TexCoord", var->name) == 0) + && (size > state->Const.MaxTextureCoords)) { + YYLTYPE loc = this->get_location(); + +*************** +*** 2022,2028 **** + delete var; + var = NULL; + } else if (state->extensions->ARB_fragment_coord_conventions +! && strcmp(var->name, "gl_FragCoord") == 0 + && earlier->type == var->type + && earlier->mode == var->mode) { + /* Allow redeclaration of gl_FragCoord for ARB_fcc layout +--- 2023,2029 ---- + delete var; + var = NULL; + } else if (state->extensions->ARB_fragment_coord_conventions +! && safe_strcmp(var->name, "gl_FragCoord") == 0 + && earlier->type == var->type + && earlier->mode == var->mode) { + /* Allow redeclaration of gl_FragCoord for ARB_fcc layout +*************** +*** 2336,2342 **** + } + + /* Verify the return type of main() */ +! if (strcmp(name, "main") == 0) { + if (! return_type->is_void()) { + YYLTYPE loc = this->get_location(); + +--- 2337,2343 ---- + } + + /* Verify the return type of main() */ +! if (safe_strcmp(name, "main") == 0) { + if (! return_type->is_void()) { + YYLTYPE loc = this->get_location(); + +diff -c -r Mesa-7.9/src/glsl/glcpp/glcpp-parse.c MesaLib/src/glsl/glcpp/glcpp-parse.c +*** Mesa-7.9/src/glsl/glcpp/glcpp-parse.c Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/glcpp/glcpp-parse.c Fri Oct 22 15:30:59 2010 +*************** +*** 96,106 **** +--- 96,114 ---- + #include + #include + #include ++ /* The #include of inttypes.h causes problems on Windows */ ++ #ifndef _MSC_VER + #include ++ #endif ++ /* Windows headers do not define PRIiMAX */ ++ #ifdef _MSC_VER ++ #define PRIiMAX "I64i" ++ #endif + + #include "glcpp.h" + #include "main/core.h" /* for struct gl_extensions */ + #include "main/mtypes.h" /* for gl_api enum */ ++ #include "safe_strcmp.h" + + #define glcpp_print(stream, str) stream = talloc_strdup_append(stream, str) + #define glcpp_printf(stream, fmt, args, ...) \ +*************** +*** 2915,2921 **** + return 0; + + for (i = 0, node = list->head; node; i++, node = node->next) { +! if (strcmp (node->str, member) == 0) { + if (index) + *index = i; + return 1; +--- 2923,2929 ---- + return 0; + + for (i = 0, node = list->head; node; i++, node = node->next) { +! if (safe_strcmp (node->str, member) == 0) { + if (index) + *index = i; + return 1; +*************** +*** 2955,2961 **** + node_a && node_b; + node_a = node_a->next, node_b = node_b->next) + { +! if (strcmp (node_a->str, node_b->str)) + return 0; + } + +--- 2963,2969 ---- + node_a && node_b; + node_a = node_a->next, node_b = node_b->next) + { +! if (safe_strcmp (node_a->str, node_b->str)) + return 0; + } + +*************** +*** 3182,3189 **** + case IDENTIFIER: + case INTEGER_STRING: + case OTHER: +! if (strcmp (node_a->token->value.str, +! node_b->token->value.str)) + { + return 0; + } +--- 3190,3197 ---- + case IDENTIFIER: + case INTEGER_STRING: + case OTHER: +! if (safe_strcmp (node_a->token->value.str, +! node_b->token->value.str)) + { + return 0; + } +*************** +*** 3832,3838 **** + return 0; + + for (node = list; node; node = node->next) +! if (strcmp (node->identifier, identifier) == 0) + return 1; + + return 0; +--- 3840,3846 ---- + return 0; + + for (node = list; node; node = node->next) +! if (safe_strcmp (node->identifier, identifier) == 0) + return 1; + + return 0; +diff -c -r Mesa-7.9/src/glsl/glcpp/glcpp-parse.y MesaLib/src/glsl/glcpp/glcpp-parse.y +*** Mesa-7.9/src/glsl/glcpp/glcpp-parse.y Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/glcpp/glcpp-parse.y Fri Oct 22 15:30:47 2010 +*************** +*** 26,36 **** +--- 26,44 ---- + #include + #include + #include ++ /* The #include of inttypes.h causes problems on Windows */ ++ #ifndef _MSC_VER + #include ++ #endif ++ /* Windows headers do not define PRIiMAX */ ++ #ifdef _MSC_VER ++ #define PRIiMAX "I64i" ++ #endif + + #include "glcpp.h" + #include "main/core.h" /* for struct gl_extensions */ + #include "main/mtypes.h" /* for gl_api enum */ ++ #include "safe_strcmp.h" + + #define glcpp_print(stream, str) stream = talloc_strdup_append(stream, str) + #define glcpp_printf(stream, fmt, args, ...) \ +*************** +*** 590,596 **** + return 0; + + for (i = 0, node = list->head; node; i++, node = node->next) { +! if (strcmp (node->str, member) == 0) { + if (index) + *index = i; + return 1; +--- 598,604 ---- + return 0; + + for (i = 0, node = list->head; node; i++, node = node->next) { +! if (safe_strcmp (node->str, member) == 0) { + if (index) + *index = i; + return 1; +*************** +*** 630,636 **** + node_a && node_b; + node_a = node_a->next, node_b = node_b->next) + { +! if (strcmp (node_a->str, node_b->str)) + return 0; + } + +--- 638,644 ---- + node_a && node_b; + node_a = node_a->next, node_b = node_b->next) + { +! if (safe_strcmp (node_a->str, node_b->str)) + return 0; + } + +*************** +*** 857,864 **** + case IDENTIFIER: + case INTEGER_STRING: + case OTHER: +! if (strcmp (node_a->token->value.str, +! node_b->token->value.str)) + { + return 0; + } +--- 865,872 ---- + case IDENTIFIER: + case INTEGER_STRING: + case OTHER: +! if (safe_strcmp (node_a->token->value.str, +! node_b->token->value.str)) + { + return 0; + } +*************** +*** 1507,1513 **** + return 0; + + for (node = list; node; node = node->next) +! if (strcmp (node->identifier, identifier) == 0) + return 1; + + return 0; +--- 1515,1521 ---- + return 0; + + for (node = list; node; node = node->next) +! if (safe_strcmp (node->identifier, identifier) == 0) + return 1; + + return 0; +diff -c -r Mesa-7.9/src/glsl/glcpp/glcpp.c MesaLib/src/glsl/glcpp/glcpp.c +*** Mesa-7.9/src/glsl/glcpp/glcpp.c Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/glcpp/glcpp.c Fri Oct 22 15:31:25 2010 +*************** +*** 29,34 **** +--- 29,35 ---- + #include + #include "glcpp.h" + #include "main/mtypes.h" ++ #include "safe_strcmp.h" + + extern int yydebug; + +*************** +*** 78,84 **** + char *text; + int fd; + +! if (filename == NULL || strcmp (filename, "-") == 0) + return load_text_fd (ctx, STDIN_FILENO); + + fd = open (filename, O_RDONLY); +--- 79,85 ---- + char *text; + int fd; + +! if (filename == NULL || safe_strcmp (filename, "-") == 0) + return load_text_fd (ctx, STDIN_FILENO); + + fd = open (filename, O_RDONLY); +diff -c -r Mesa-7.9/src/glsl/glcpp/glcpp.h MesaLib/src/glsl/glcpp/glcpp.h +*** Mesa-7.9/src/glsl/glcpp/glcpp.h Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/glcpp/glcpp.h Fri Oct 22 15:32:43 2010 +*************** +*** 24,30 **** +--- 24,33 ---- + #ifndef GLCPP_H + #define GLCPP_H + ++ /* Windows does not currently have stdint.h. */ ++ #ifndef _MSC_VER + #include ++ #endif + + #include + +*************** +*** 49,55 **** +--- 52,62 ---- + + typedef union YYSTYPE + { ++ #ifndef _MSC_VER + intmax_t ival; ++ #else ++ __int64 ival; ++ #endif + char *str; + string_list_t *string_list; + token_t *token; +diff -c -r Mesa-7.9/src/glsl/glsl_parser.cpp MesaLib/src/glsl/glsl_parser.cpp +*** Mesa-7.9/src/glsl/glsl_parser.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/glsl_parser.cpp Fri Oct 22 15:33:24 2010 +*************** +*** 107,112 **** +--- 107,113 ---- + #include "ast.h" + #include "glsl_parser_extras.h" + #include "glsl_types.h" ++ #include "safe_strcmp.h" + + #define YYLEX_PARAM state->scanner + +*************** +*** 3966,3975 **** + if (state->ARB_fragment_coord_conventions_enable) { + bool got_one = false; + +! if (strcmp((yyvsp[(1) - (1)].identifier), "origin_upper_left") == 0) { + got_one = true; + (yyval.type_qualifier).q.origin_upper_left = 1; +! } else if (strcmp((yyvsp[(1) - (1)].identifier), "pixel_center_integer") == 0) { + got_one = true; + (yyval.type_qualifier).q.pixel_center_integer = 1; + } +--- 3967,3976 ---- + if (state->ARB_fragment_coord_conventions_enable) { + bool got_one = false; + +! if (safe_strcmp((yyvsp[(1) - (1)].identifier), "origin_upper_left") == 0) { + got_one = true; + (yyval.type_qualifier).q.origin_upper_left = 1; +! } else if (safe_strcmp((yyvsp[(1) - (1)].identifier), "pixel_center_integer") == 0) { + got_one = true; + (yyval.type_qualifier).q.pixel_center_integer = 1; + } +diff -c -r Mesa-7.9/src/glsl/glsl_parser_extras.cpp MesaLib/src/glsl/glsl_parser_extras.cpp +*** Mesa-7.9/src/glsl/glsl_parser_extras.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/glsl_parser_extras.cpp Fri Oct 22 15:33:37 2010 +*************** +*** 35,40 **** +--- 35,41 ---- + #include "glsl_parser.h" + #include "ir_optimization.h" + #include "loop_analysis.h" ++ #include "safe_strcmp.h" + + _mesa_glsl_parse_state::_mesa_glsl_parse_state(struct __GLcontextRec *ctx, + GLenum target, void *mem_ctx) +*************** +*** 148,160 **** + extension_warn + } ext_mode; + +! if (strcmp(behavior, "warn") == 0) { + ext_mode = extension_warn; +! } else if (strcmp(behavior, "require") == 0) { + ext_mode = extension_require; +! } else if (strcmp(behavior, "enable") == 0) { + ext_mode = extension_enable; +! } else if (strcmp(behavior, "disable") == 0) { + ext_mode = extension_disable; + } else { + _mesa_glsl_error(behavior_locp, state, +--- 149,161 ---- + extension_warn + } ext_mode; + +! if (safe_strcmp(behavior, "warn") == 0) { + ext_mode = extension_warn; +! } else if (safe_strcmp(behavior, "require") == 0) { + ext_mode = extension_require; +! } else if (safe_strcmp(behavior, "enable") == 0) { + ext_mode = extension_enable; +! } else if (safe_strcmp(behavior, "disable") == 0) { + ext_mode = extension_disable; + } else { + _mesa_glsl_error(behavior_locp, state, +*************** +*** 165,178 **** + + bool unsupported = false; + +! if (strcmp(name, "all") == 0) { + if ((ext_mode == extension_enable) || (ext_mode == extension_require)) { + _mesa_glsl_error(name_locp, state, "Cannot %s all extensions", + (ext_mode == extension_enable) + ? "enable" : "require"); + return false; + } +! } else if (strcmp(name, "GL_ARB_draw_buffers") == 0) { + /* This extension is only supported in fragment shaders. + */ + if (state->target != fragment_shader) { +--- 166,179 ---- + + bool unsupported = false; + +! if (safe_strcmp(name, "all") == 0) { + if ((ext_mode == extension_enable) || (ext_mode == extension_require)) { + _mesa_glsl_error(name_locp, state, "Cannot %s all extensions", + (ext_mode == extension_enable) + ? "enable" : "require"); + return false; + } +! } else if (safe_strcmp(name, "GL_ARB_draw_buffers") == 0) { + /* This extension is only supported in fragment shaders. + */ + if (state->target != fragment_shader) { +*************** +*** 181,197 **** + state->ARB_draw_buffers_enable = (ext_mode != extension_disable); + state->ARB_draw_buffers_warn = (ext_mode == extension_warn); + } +! } else if (strcmp(name, "GL_ARB_fragment_coord_conventions") == 0) { + state->ARB_fragment_coord_conventions_enable = + (ext_mode != extension_disable); + state->ARB_fragment_coord_conventions_warn = + (ext_mode == extension_warn); + + unsupported = !state->extensions->ARB_fragment_coord_conventions; +! } else if (strcmp(name, "GL_ARB_texture_rectangle") == 0) { + state->ARB_texture_rectangle_enable = (ext_mode != extension_disable); + state->ARB_texture_rectangle_warn = (ext_mode == extension_warn); +! } else if (strcmp(name, "GL_EXT_texture_array") == 0) { + state->EXT_texture_array_enable = (ext_mode != extension_disable); + state->EXT_texture_array_warn = (ext_mode == extension_warn); + +--- 182,198 ---- + state->ARB_draw_buffers_enable = (ext_mode != extension_disable); + state->ARB_draw_buffers_warn = (ext_mode == extension_warn); + } +! } else if (safe_strcmp(name, "GL_ARB_fragment_coord_conventions") == 0) { + state->ARB_fragment_coord_conventions_enable = + (ext_mode != extension_disable); + state->ARB_fragment_coord_conventions_warn = + (ext_mode == extension_warn); + + unsupported = !state->extensions->ARB_fragment_coord_conventions; +! } else if (safe_strcmp(name, "GL_ARB_texture_rectangle") == 0) { + state->ARB_texture_rectangle_enable = (ext_mode != extension_disable); + state->ARB_texture_rectangle_warn = (ext_mode == extension_warn); +! } else if (safe_strcmp(name, "GL_EXT_texture_array") == 0) { + state->EXT_texture_array_enable = (ext_mode != extension_disable); + state->EXT_texture_array_warn = (ext_mode == extension_warn); + +diff -c -r Mesa-7.9/src/glsl/glsl_types.cpp MesaLib/src/glsl/glsl_types.cpp +*** Mesa-7.9/src/glsl/glsl_types.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/glsl_types.cpp Fri Oct 22 15:33:51 2010 +*************** +*** 27,32 **** +--- 27,33 ---- + #include "glsl_symbol_table.h" + #include "glsl_parser_extras.h" + #include "glsl_types.h" ++ #include "safe_strcmp.h" + #include "builtin_types.h" + extern "C" { + #include "program/hash_table.h" +*************** +*** 374,380 **** + /* Return zero is the types match (there is zero difference) or non-zero + * otherwise. + */ +! if (strcmp(key1->name, key2->name) != 0) + return 1; + + if (key1->length != key2->length) +--- 375,381 ---- + /* Return zero is the types match (there is zero difference) or non-zero + * otherwise. + */ +! if (safe_strcmp(key1->name, key2->name) != 0) + return 1; + + if (key1->length != key2->length) +*************** +*** 383,389 **** + for (unsigned i = 0; i < key1->length; i++) { + if (key1->fields.structure[i].type != key2->fields.structure[i].type) + return 1; +! if (strcmp(key1->fields.structure[i].name, + key2->fields.structure[i].name) != 0) + return 1; + } +--- 384,390 ---- + for (unsigned i = 0; i < key1->length; i++) { + if (key1->fields.structure[i].type != key2->fields.structure[i].type) + return 1; +! if (safe_strcmp(key1->fields.structure[i].name, + key2->fields.structure[i].name) != 0) + return 1; + } +*************** +*** 433,439 **** + + assert(t->base_type == GLSL_TYPE_STRUCT); + assert(t->length == num_fields); +! assert(strcmp(t->name, name) == 0); + + return t; + } +--- 434,440 ---- + + assert(t->base_type == GLSL_TYPE_STRUCT); + assert(t->length == num_fields); +! assert(safe_strcmp(t->name, name) == 0); + + return t; + } +*************** +*** 446,452 **** + return error_type; + + for (unsigned i = 0; i < this->length; i++) { +! if (strcmp(name, this->fields.structure[i].name) == 0) + return this->fields.structure[i].type; + } + +--- 447,453 ---- + return error_type; + + for (unsigned i = 0; i < this->length; i++) { +! if (safe_strcmp(name, this->fields.structure[i].name) == 0) + return this->fields.structure[i].type; + } + +*************** +*** 461,467 **** + return -1; + + for (unsigned i = 0; i < this->length; i++) { +! if (strcmp(name, this->fields.structure[i].name) == 0) + return i; + } + +--- 462,468 ---- + return -1; + + for (unsigned i = 0; i < this->length; i++) { +! if (safe_strcmp(name, this->fields.structure[i].name) == 0) + return i; + } + +diff -c -r Mesa-7.9/src/glsl/hir_field_selection.cpp MesaLib/src/glsl/hir_field_selection.cpp +*** Mesa-7.9/src/glsl/hir_field_selection.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/hir_field_selection.cpp Fri Oct 22 15:34:11 2010 +*************** +*** 26,31 **** +--- 26,32 ---- + #include "glsl_parser_extras.h" + #include "ast.h" + #include "glsl_types.h" ++ #include "safe_strcmp.h" + + ir_rvalue * + _mesa_ast_field_selection_to_hir(const ast_expression *expr, +*************** +*** 81,87 **** + const char *method; + method = call->subexpressions[0]->primary_expression.identifier; + +! if (op->type->is_array() && strcmp(method, "length") == 0) { + if (!call->expressions.is_empty()) + _mesa_glsl_error(&loc, state, "length method takes no arguments."); + +--- 82,88 ---- + const char *method; + method = call->subexpressions[0]->primary_expression.identifier; + +! if (op->type->is_array() && safe_strcmp(method, "length") == 0) { + if (!call->expressions.is_empty()) + _mesa_glsl_error(&loc, state, "length method takes no arguments."); + +diff -c -r Mesa-7.9/src/glsl/ir.cpp MesaLib/src/glsl/ir.cpp +*** Mesa-7.9/src/glsl/ir.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/ir.cpp Fri Oct 22 15:34:20 2010 +*************** +*** 25,30 **** +--- 25,31 ---- + #include "ir.h" + #include "ir_visitor.h" + #include "glsl_types.h" ++ #include "safe_strcmp.h" + + ir_rvalue::ir_rvalue() + { +*************** +*** 338,344 **** + { + const int operator_count = sizeof(operator_strs) / sizeof(operator_strs[0]); + for (int op = 0; op < operator_count; op++) { +! if (strcmp(str, operator_strs[op]) == 0) + return (ir_expression_operation) op; + } + return (ir_expression_operation) -1; +--- 339,345 ---- + { + const int operator_count = sizeof(operator_strs) / sizeof(operator_strs[0]); + for (int op = 0; op < operator_count; op++) { +! if (safe_strcmp(str, operator_strs[op]) == 0) + return (ir_expression_operation) op; + } + return (ir_expression_operation) -1; +*************** +*** 878,884 **** + { + const int count = sizeof(tex_opcode_strs) / sizeof(tex_opcode_strs[0]); + for (int op = 0; op < count; op++) { +! if (strcmp(str, tex_opcode_strs[op]) == 0) + return (ir_texture_opcode) op; + } + return (ir_texture_opcode) -1; +--- 879,885 ---- + { + const int count = sizeof(tex_opcode_strs) / sizeof(tex_opcode_strs[0]); + for (int op = 0; op < count; op++) { +! if (safe_strcmp(str, tex_opcode_strs[op]) == 0) + return (ir_texture_opcode) op; + } + return (ir_texture_opcode) -1; +diff -c -r Mesa-7.9/src/glsl/ir_constant_expression.cpp MesaLib/src/glsl/ir_constant_expression.cpp +*** Mesa-7.9/src/glsl/ir_constant_expression.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/ir_constant_expression.cpp Fri Oct 22 15:34:40 2010 +*************** +*** 38,43 **** +--- 38,44 ---- + #include "ir.h" + #include "ir_visitor.h" + #include "glsl_types.h" ++ #include "safe_strcmp.h" + + static float + dot(ir_constant *op0, ir_constant *op1) +*************** +*** 850,880 **** + memset(&data, 0, sizeof(data)); + + const char *callee = this->callee_name(); +! if (strcmp(callee, "abs") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_abs, type, op[0], NULL); +! } else if (strcmp(callee, "all") == 0) { + assert(op[0]->type->is_boolean()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + if (!op[0]->value.b[c]) + return new(mem_ctx) ir_constant(false); + } + return new(mem_ctx) ir_constant(true); +! } else if (strcmp(callee, "any") == 0) { + assert(op[0]->type->is_boolean()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + if (op[0]->value.b[c]) + return new(mem_ctx) ir_constant(true); + } + return new(mem_ctx) ir_constant(false); +! } else if (strcmp(callee, "acos") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = acosf(op[0]->value.f[c]); +! } else if (strcmp(callee, "asin") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = asinf(op[0]->value.f[c]); +! } else if (strcmp(callee, "atan") == 0) { + assert(op[0]->type->is_float()); + if (num_parameters == 2) { + assert(op[1]->type->is_float()); +--- 851,881 ---- + memset(&data, 0, sizeof(data)); + + const char *callee = this->callee_name(); +! if (safe_strcmp(callee, "abs") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_abs, type, op[0], NULL); +! } else if (safe_strcmp(callee, "all") == 0) { + assert(op[0]->type->is_boolean()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + if (!op[0]->value.b[c]) + return new(mem_ctx) ir_constant(false); + } + return new(mem_ctx) ir_constant(true); +! } else if (safe_strcmp(callee, "any") == 0) { + assert(op[0]->type->is_boolean()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + if (op[0]->value.b[c]) + return new(mem_ctx) ir_constant(true); + } + return new(mem_ctx) ir_constant(false); +! } else if (safe_strcmp(callee, "acos") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = acosf(op[0]->value.f[c]); +! } else if (safe_strcmp(callee, "asin") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = asinf(op[0]->value.f[c]); +! } else if (safe_strcmp(callee, "atan") == 0) { + assert(op[0]->type->is_float()); + if (num_parameters == 2) { + assert(op[1]->type->is_float()); +*************** +*** 884,894 **** + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = atanf(op[0]->value.f[c]); + } +! } else if (strcmp(callee, "dFdx") == 0 || strcmp(callee, "dFdy") == 0) { + return ir_constant::zero(mem_ctx, this->type); +! } else if (strcmp(callee, "ceil") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_ceil, type, op[0], NULL); +! } else if (strcmp(callee, "clamp") == 0) { + assert(num_parameters == 3); + unsigned c1_inc = op[1]->type->is_scalar() ? 0 : 1; + unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; +--- 885,895 ---- + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = atanf(op[0]->value.f[c]); + } +! } else if (safe_strcmp(callee, "dFdx") == 0 || safe_strcmp(callee, "dFdy") == 0) { + return ir_constant::zero(mem_ctx, this->type); +! } else if (safe_strcmp(callee, "ceil") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_ceil, type, op[0], NULL); +! } else if (safe_strcmp(callee, "clamp") == 0) { + assert(num_parameters == 3); + unsigned c1_inc = op[1]->type->is_scalar() ? 0 : 1; + unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; +*************** +*** 913,931 **** + assert(!"Should not get here."); + } + } +! } else if (strcmp(callee, "cos") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_cos, type, op[0], NULL); +! } else if (strcmp(callee, "cosh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = coshf(op[0]->value.f[c]); +! } else if (strcmp(callee, "cross") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_cross, type, op[0], op[1]); +! } else if (strcmp(callee, "degrees") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = 180.0/M_PI * op[0]->value.f[c]; +! } else if (strcmp(callee, "distance") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + float length_squared = 0.0; + for (unsigned c = 0; c < op[0]->type->components(); c++) { +--- 914,932 ---- + assert(!"Should not get here."); + } + } +! } else if (safe_strcmp(callee, "cos") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_cos, type, op[0], NULL); +! } else if (safe_strcmp(callee, "cosh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = coshf(op[0]->value.f[c]); +! } else if (safe_strcmp(callee, "cross") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_cross, type, op[0], op[1]); +! } else if (safe_strcmp(callee, "degrees") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = 180.0/M_PI * op[0]->value.f[c]; +! } else if (safe_strcmp(callee, "distance") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + float length_squared = 0.0; + for (unsigned c = 0; c < op[0]->type->components(); c++) { +*************** +*** 933,941 **** + length_squared += t * t; + } + return new(mem_ctx) ir_constant(sqrtf(length_squared)); +! } else if (strcmp(callee, "dot") == 0) { + return new(mem_ctx) ir_constant(dot(op[0], op[1])); +! } else if (strcmp(callee, "equal") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +--- 934,942 ---- + length_squared += t * t; + } + return new(mem_ctx) ir_constant(sqrtf(length_squared)); +! } else if (safe_strcmp(callee, "dot") == 0) { + return new(mem_ctx) ir_constant(dot(op[0], op[1])); +! } else if (safe_strcmp(callee, "equal") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +*************** +*** 955,976 **** + assert(!"Should not get here."); + } + } +! } else if (strcmp(callee, "exp") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_exp, type, op[0], NULL); +! } else if (strcmp(callee, "exp2") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_exp2, type, op[0], NULL); +! } else if (strcmp(callee, "faceforward") == 0) { + if (dot(op[2], op[1]) < 0) + return op[0]; + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = -op[0]->value.f[c]; +! } else if (strcmp(callee, "floor") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_floor, type, op[0], NULL); +! } else if (strcmp(callee, "fract") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_fract, type, op[0], NULL); +! } else if (strcmp(callee, "fwidth") == 0) { + return ir_constant::zero(mem_ctx, this->type); +! } else if (strcmp(callee, "greaterThan") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +--- 956,977 ---- + assert(!"Should not get here."); + } + } +! } else if (safe_strcmp(callee, "exp") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_exp, type, op[0], NULL); +! } else if (safe_strcmp(callee, "exp2") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_exp2, type, op[0], NULL); +! } else if (safe_strcmp(callee, "faceforward") == 0) { + if (dot(op[2], op[1]) < 0) + return op[0]; + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = -op[0]->value.f[c]; +! } else if (safe_strcmp(callee, "floor") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_floor, type, op[0], NULL); +! } else if (safe_strcmp(callee, "fract") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_fract, type, op[0], NULL); +! } else if (safe_strcmp(callee, "fwidth") == 0) { + return ir_constant::zero(mem_ctx, this->type); +! } else if (safe_strcmp(callee, "greaterThan") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +*************** +*** 987,993 **** + assert(!"Should not get here."); + } + } +! } else if (strcmp(callee, "greaterThanEqual") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +--- 988,994 ---- + assert(!"Should not get here."); + } + } +! } else if (safe_strcmp(callee, "greaterThanEqual") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +*************** +*** 1004,1014 **** + assert(!"Should not get here."); + } + } +! } else if (strcmp(callee, "inversesqrt") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_rsq, type, op[0], NULL); +! } else if (strcmp(callee, "length") == 0) { + return new(mem_ctx) ir_constant(sqrtf(dot(op[0], op[0]))); +! } else if (strcmp(callee, "lessThan") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +--- 1005,1015 ---- + assert(!"Should not get here."); + } + } +! } else if (safe_strcmp(callee, "inversesqrt") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_rsq, type, op[0], NULL); +! } else if (safe_strcmp(callee, "length") == 0) { + return new(mem_ctx) ir_constant(sqrtf(dot(op[0], op[0]))); +! } else if (safe_strcmp(callee, "lessThan") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +*************** +*** 1025,1031 **** + assert(!"Should not get here."); + } + } +! } else if (strcmp(callee, "lessThanEqual") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +--- 1026,1032 ---- + assert(!"Should not get here."); + } + } +! } else if (safe_strcmp(callee, "lessThanEqual") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +*************** +*** 1042,1060 **** + assert(!"Should not get here."); + } + } +! } else if (strcmp(callee, "log") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_log, type, op[0], NULL); +! } else if (strcmp(callee, "log2") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_log2, type, op[0], NULL); +! } else if (strcmp(callee, "matrixCompMult") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[0]->value.f[c] * op[1]->value.f[c]; +! } else if (strcmp(callee, "max") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_max, type, op[0], op[1]); +! } else if (strcmp(callee, "min") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_min, type, op[0], op[1]); +! } else if (strcmp(callee, "mix") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + if (op[2]->type->is_float()) { + unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; +--- 1043,1061 ---- + assert(!"Should not get here."); + } + } +! } else if (safe_strcmp(callee, "log") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_log, type, op[0], NULL); +! } else if (safe_strcmp(callee, "log2") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_log2, type, op[0], NULL); +! } else if (safe_strcmp(callee, "matrixCompMult") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[0]->value.f[c] * op[1]->value.f[c]; +! } else if (safe_strcmp(callee, "max") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_max, type, op[0], op[1]); +! } else if (safe_strcmp(callee, "min") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_min, type, op[0], op[1]); +! } else if (safe_strcmp(callee, "mix") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + if (op[2]->type->is_float()) { + unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; +*************** +*** 1068,1076 **** + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[op[2]->value.b[c] ? 1 : 0]->value.f[c]; + } +! } else if (strcmp(callee, "mod") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_mod, type, op[0], op[1]); +! } else if (strcmp(callee, "normalize") == 0) { + assert(op[0]->type->is_float()); + float length = sqrtf(dot(op[0], op[0])); + +--- 1069,1077 ---- + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[op[2]->value.b[c] ? 1 : 0]->value.f[c]; + } +! } else if (safe_strcmp(callee, "mod") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_mod, type, op[0], op[1]); +! } else if (safe_strcmp(callee, "normalize") == 0) { + assert(op[0]->type->is_float()); + float length = sqrtf(dot(op[0], op[0])); + +*************** +*** 1079,1087 **** + + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[0]->value.f[c] / length; +! } else if (strcmp(callee, "not") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_logic_not, type, op[0], NULL); +! } else if (strcmp(callee, "notEqual") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +--- 1080,1088 ---- + + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[0]->value.f[c] / length; +! } else if (safe_strcmp(callee, "not") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_logic_not, type, op[0], NULL); +! } else if (safe_strcmp(callee, "notEqual") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { +*************** +*** 1101,1107 **** + assert(!"Should not get here."); + } + } +! } else if (strcmp(callee, "outerProduct") == 0) { + assert(op[0]->type->is_vector() && op[1]->type->is_vector()); + const unsigned m = op[0]->type->vector_elements; + const unsigned n = op[1]->type->vector_elements; +--- 1102,1108 ---- + assert(!"Should not get here."); + } + } +! } else if (safe_strcmp(callee, "outerProduct") == 0) { + assert(op[0]->type->is_vector() && op[1]->type->is_vector()); + const unsigned m = op[0]->type->vector_elements; + const unsigned n = op[1]->type->vector_elements; +*************** +*** 1110,1127 **** + data.f[i+m*j] = op[0]->value.f[i] * op[1]->value.f[j]; + } + } +! } else if (strcmp(callee, "pow") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_pow, type, op[0], op[1]); +! } else if (strcmp(callee, "radians") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = M_PI/180.0 * op[0]->value.f[c]; +! } else if (strcmp(callee, "reflect") == 0) { + assert(op[0]->type->is_float()); + float dot_NI = dot(op[1], op[0]); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[0]->value.f[c] - 2 * dot_NI * op[1]->value.f[c]; +! } else if (strcmp(callee, "refract") == 0) { + const float eta = op[2]->value.f[0]; + const float dot_NI = dot(op[1], op[0]); + const float k = 1.0 - eta * eta * (1.0 - dot_NI * dot_NI); +--- 1111,1128 ---- + data.f[i+m*j] = op[0]->value.f[i] * op[1]->value.f[j]; + } + } +! } else if (safe_strcmp(callee, "pow") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_pow, type, op[0], op[1]); +! } else if (safe_strcmp(callee, "radians") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = M_PI/180.0 * op[0]->value.f[c]; +! } else if (safe_strcmp(callee, "reflect") == 0) { + assert(op[0]->type->is_float()); + float dot_NI = dot(op[1], op[0]); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[0]->value.f[c] - 2 * dot_NI * op[1]->value.f[c]; +! } else if (safe_strcmp(callee, "refract") == 0) { + const float eta = op[2]->value.f[0]; + const float dot_NI = dot(op[1], op[0]); + const float k = 1.0 - eta * eta * (1.0 - dot_NI * dot_NI); +*************** +*** 1133,1147 **** + * op[1]->value.f[c]; + } + } +! } else if (strcmp(callee, "sign") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_sign, type, op[0], NULL); +! } else if (strcmp(callee, "sin") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_sin, type, op[0], NULL); +! } else if (strcmp(callee, "sinh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = sinhf(op[0]->value.f[c]); +! } else if (strcmp(callee, "smoothstep") == 0) { + assert(num_parameters == 3); + assert(op[1]->type == op[0]->type); + unsigned edge_inc = op[0]->type->is_scalar() ? 0 : 1; +--- 1134,1148 ---- + * op[1]->value.f[c]; + } + } +! } else if (safe_strcmp(callee, "sign") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_sign, type, op[0], NULL); +! } else if (safe_strcmp(callee, "sin") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_sin, type, op[0], NULL); +! } else if (safe_strcmp(callee, "sinh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = sinhf(op[0]->value.f[c]); +! } else if (safe_strcmp(callee, "smoothstep") == 0) { + assert(num_parameters == 3); + assert(op[1]->type == op[0]->type); + unsigned edge_inc = op[0]->type->is_scalar() ? 0 : 1; +*************** +*** 1157,1179 **** + data.f[c] = t * t * (3 - 2 * t); + } + } +! } else if (strcmp(callee, "sqrt") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_sqrt, type, op[0], NULL); +! } else if (strcmp(callee, "step") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + /* op[0] (edge) may be either a scalar or a vector */ + const unsigned c0_inc = op[0]->type->is_scalar() ? 0 : 1; + for (unsigned c = 0, c0 = 0; c < type->components(); c0 += c0_inc, c++) + data.f[c] = (op[1]->value.f[c] < op[0]->value.f[c0]) ? 0.0 : 1.0; +! } else if (strcmp(callee, "tan") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = tanf(op[0]->value.f[c]); +! } else if (strcmp(callee, "tanh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = tanhf(op[0]->value.f[c]); +! } else if (strcmp(callee, "transpose") == 0) { + assert(op[0]->type->is_matrix()); + const unsigned n = op[0]->type->vector_elements; + const unsigned m = op[0]->type->matrix_columns; +--- 1158,1180 ---- + data.f[c] = t * t * (3 - 2 * t); + } + } +! } else if (safe_strcmp(callee, "sqrt") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_sqrt, type, op[0], NULL); +! } else if (safe_strcmp(callee, "step") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + /* op[0] (edge) may be either a scalar or a vector */ + const unsigned c0_inc = op[0]->type->is_scalar() ? 0 : 1; + for (unsigned c = 0, c0 = 0; c < type->components(); c0 += c0_inc, c++) + data.f[c] = (op[1]->value.f[c] < op[0]->value.f[c0]) ? 0.0 : 1.0; +! } else if (safe_strcmp(callee, "tan") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = tanf(op[0]->value.f[c]); +! } else if (safe_strcmp(callee, "tanh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = tanhf(op[0]->value.f[c]); +! } else if (safe_strcmp(callee, "transpose") == 0) { + assert(op[0]->type->is_matrix()); + const unsigned n = op[0]->type->vector_elements; + const unsigned m = op[0]->type->matrix_columns; +diff -c -r Mesa-7.9/src/glsl/ir_dead_functions.cpp MesaLib/src/glsl/ir_dead_functions.cpp +*** Mesa-7.9/src/glsl/ir_dead_functions.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/ir_dead_functions.cpp Fri Oct 22 15:34:54 2010 +*************** +*** 31,36 **** +--- 31,37 ---- + #include "ir_visitor.h" + #include "ir_expression_flattening.h" + #include "glsl_types.h" ++ #include "safe_strcmp.h" + + class signature_entry : public exec_node + { +*************** +*** 90,96 **** + { + signature_entry *entry = this->get_signature_entry(ir); + +! if (strcmp(ir->function_name(), "main") == 0) { + entry->used = true; + } + +--- 91,97 ---- + { + signature_entry *entry = this->get_signature_entry(ir); + +! if (safe_strcmp(ir->function_name(), "main") == 0) { + entry->used = true; + } + +diff -c -r Mesa-7.9/src/glsl/ir_function_inlining.cpp MesaLib/src/glsl/ir_function_inlining.cpp +*** Mesa-7.9/src/glsl/ir_function_inlining.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/ir_function_inlining.cpp Fri Oct 22 15:35:58 2010 +*************** +*** 27,33 **** + * Replaces calls to functions with the body of the function. + */ + +! #include + #include "ir.h" + #include "ir_visitor.h" + #include "ir_function_inlining.h" +--- 27,34 ---- + * Replaces calls to functions with the body of the function. + */ + +! /* The use of inttypes.h seems to be unnecessary and causes problems on Windows. */ +! /* #include */ + #include "ir.h" + #include "ir_visitor.h" + #include "ir_function_inlining.h" +diff -c -r Mesa-7.9/src/glsl/ir_lower_jumps.cpp MesaLib/src/glsl/ir_lower_jumps.cpp +*** Mesa-7.9/src/glsl/ir_lower_jumps.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/ir_lower_jumps.cpp Fri Oct 22 15:36:16 2010 +*************** +*** 28,33 **** +--- 28,34 ---- + #include "glsl_types.h" + #include + #include "ir.h" ++ #include "safe_strcmp.h" + + enum jump_strength + { +*************** +*** 125,131 **** + this->return_flag = 0; + this->return_value = 0; + this->nesting_depth = 0; +! this->is_main = this->signature && (strcmp(this->signature->function_name(), "main") == 0); + } + + ir_variable* get_return_flag() +--- 126,132 ---- + this->return_flag = 0; + this->return_value = 0; + this->nesting_depth = 0; +! this->is_main = this->signature && (safe_strcmp(this->signature->function_name(), "main") == 0); + } + + ir_variable* get_return_flag() +diff -c -r Mesa-7.9/src/glsl/ir_reader.cpp MesaLib/src/glsl/ir_reader.cpp +*** Mesa-7.9/src/glsl/ir_reader.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/ir_reader.cpp Fri Oct 22 15:36:27 2010 +*************** +*** 31,36 **** +--- 31,37 ---- + #include "glsl_parser_extras.h" + #include "glsl_types.h" + #include "s_expression.h" ++ #include "safe_strcmp.h" + + const static bool debug = false; + +*************** +*** 128,134 **** + ir_read_error(st, expr, "expected type (array ...) or (struct ...)"); + return NULL; + } +! if (strcmp(type_sym->value(), "array") == 0) { + if (list->length() != 3) { + ir_read_error(st, expr, "expected type (array )"); + return NULL; +--- 129,135 ---- + ir_read_error(st, expr, "expected type (array ...) or (struct ...)"); + return NULL; + } +! if (safe_strcmp(type_sym->value(), "array") == 0) { + if (list->length() != 3) { + ir_read_error(st, expr, "expected type (array )"); + return NULL; +*************** +*** 150,156 **** + } + + return glsl_type::get_array_instance(base_type, size->value()); +! } else if (strcmp(type_sym->value(), "struct") == 0) { + assert(false); // FINISHME + } else { + ir_read_error(st, expr, "expected (array ...) or (struct ...); " +--- 151,157 ---- + } + + return glsl_type::get_array_instance(base_type, size->value()); +! } else if (safe_strcmp(type_sym->value(), "struct") == 0) { + assert(false); // FINISHME + } else { + ir_read_error(st, expr, "expected (array ...) or (struct ...); " +*************** +*** 189,195 **** + continue; // not a (function ...); ignore it. + + s_symbol *tag = SX_AS_SYMBOL(sub->subexpressions.get_head()); +! if (tag == NULL || strcmp(tag->value(), "function") != 0) + continue; // not a (function ...); ignore it. + + ir_function *f = read_function(st, sub, true); +--- 190,196 ---- + continue; // not a (function ...); ignore it. + + s_symbol *tag = SX_AS_SYMBOL(sub->subexpressions.get_head()); +! if (tag == NULL || safe_strcmp(tag->value(), "function") != 0) + continue; // not a (function ...); ignore it. + + ir_function *f = read_function(st, sub, true); +*************** +*** 233,239 **** + } + + s_symbol *tag = SX_AS_SYMBOL(siglist->subexpressions.get_head()); +! if (tag == NULL || strcmp(tag->value(), "signature") != 0) { + ir_read_error(st, siglist, "Expected (signature ...)"); + return NULL; + } +--- 234,240 ---- + } + + s_symbol *tag = SX_AS_SYMBOL(siglist->subexpressions.get_head()); +! if (tag == NULL || safe_strcmp(tag->value(), "signature") != 0) { + ir_read_error(st, siglist, "Expected (signature ...)"); + return NULL; + } +*************** +*** 267,273 **** + return; + } + s_symbol *paramtag = SX_AS_SYMBOL(paramlist->subexpressions.get_head()); +! if (paramtag == NULL || strcmp(paramtag->value(), "parameters") != 0) { + ir_read_error(st, paramlist, "Expected (parameters ...)"); + return; + } +--- 268,274 ---- + return; + } + s_symbol *paramtag = SX_AS_SYMBOL(paramlist->subexpressions.get_head()); +! if (paramtag == NULL || safe_strcmp(paramtag->value(), "parameters") != 0) { + ir_read_error(st, paramlist, "Expected (parameters ...)"); + return; + } +*************** +*** 364,372 **** + void *ctx = st; + s_symbol *symbol = SX_AS_SYMBOL(expr); + if (symbol != NULL) { +! if (strcmp(symbol->value(), "break") == 0 && loop_ctx != NULL) + return new(ctx) ir_loop_jump(ir_loop_jump::jump_break); +! if (strcmp(symbol->value(), "continue") == 0 && loop_ctx != NULL) + return new(ctx) ir_loop_jump(ir_loop_jump::jump_continue); + } + +--- 365,373 ---- + void *ctx = st; + s_symbol *symbol = SX_AS_SYMBOL(expr); + if (symbol != NULL) { +! if (safe_strcmp(symbol->value(), "break") == 0 && loop_ctx != NULL) + return new(ctx) ir_loop_jump(ir_loop_jump::jump_break); +! if (safe_strcmp(symbol->value(), "continue") == 0 && loop_ctx != NULL) + return new(ctx) ir_loop_jump(ir_loop_jump::jump_continue); + } + +*************** +*** 383,399 **** + } + + ir_instruction *inst = NULL; +! if (strcmp(tag->value(), "declare") == 0) { + inst = read_declaration(st, list); +! } else if (strcmp(tag->value(), "assign") == 0) { + inst = read_assignment(st, list); +! } else if (strcmp(tag->value(), "if") == 0) { + inst = read_if(st, list, loop_ctx); +! } else if (strcmp(tag->value(), "loop") == 0) { + inst = read_loop(st, list); +! } else if (strcmp(tag->value(), "return") == 0) { + inst = read_return(st, list); +! } else if (strcmp(tag->value(), "function") == 0) { + inst = read_function(st, list, false); + } else { + inst = read_rvalue(st, list); +--- 384,400 ---- + } + + ir_instruction *inst = NULL; +! if (safe_strcmp(tag->value(), "declare") == 0) { + inst = read_declaration(st, list); +! } else if (safe_strcmp(tag->value(), "assign") == 0) { + inst = read_assignment(st, list); +! } else if (safe_strcmp(tag->value(), "if") == 0) { + inst = read_if(st, list, loop_ctx); +! } else if (safe_strcmp(tag->value(), "loop") == 0) { + inst = read_loop(st, list); +! } else if (safe_strcmp(tag->value(), "return") == 0) { + inst = read_return(st, list); +! } else if (safe_strcmp(tag->value(), "function") == 0) { + inst = read_function(st, list, false); + } else { + inst = read_rvalue(st, list); +*************** +*** 443,467 **** + } + + // FINISHME: Check for duplicate/conflicting qualifiers. +! if (strcmp(qualifier->value(), "centroid") == 0) { + var->centroid = 1; +! } else if (strcmp(qualifier->value(), "invariant") == 0) { + var->invariant = 1; +! } else if (strcmp(qualifier->value(), "uniform") == 0) { + var->mode = ir_var_uniform; +! } else if (strcmp(qualifier->value(), "auto") == 0) { + var->mode = ir_var_auto; +! } else if (strcmp(qualifier->value(), "in") == 0) { + var->mode = ir_var_in; +! } else if (strcmp(qualifier->value(), "out") == 0) { + var->mode = ir_var_out; +! } else if (strcmp(qualifier->value(), "inout") == 0) { + var->mode = ir_var_inout; +! } else if (strcmp(qualifier->value(), "smooth") == 0) { + var->interpolation = ir_var_smooth; +! } else if (strcmp(qualifier->value(), "flat") == 0) { + var->interpolation = ir_var_flat; +! } else if (strcmp(qualifier->value(), "noperspective") == 0) { + var->interpolation = ir_var_noperspective; + } else { + ir_read_error(st, list, "unknown qualifier: %s", qualifier->value()); +--- 444,468 ---- + } + + // FINISHME: Check for duplicate/conflicting qualifiers. +! if (safe_strcmp(qualifier->value(), "centroid") == 0) { + var->centroid = 1; +! } else if (safe_strcmp(qualifier->value(), "invariant") == 0) { + var->invariant = 1; +! } else if (safe_strcmp(qualifier->value(), "uniform") == 0) { + var->mode = ir_var_uniform; +! } else if (safe_strcmp(qualifier->value(), "auto") == 0) { + var->mode = ir_var_auto; +! } else if (safe_strcmp(qualifier->value(), "in") == 0) { + var->mode = ir_var_in; +! } else if (safe_strcmp(qualifier->value(), "out") == 0) { + var->mode = ir_var_out; +! } else if (safe_strcmp(qualifier->value(), "inout") == 0) { + var->mode = ir_var_inout; +! } else if (safe_strcmp(qualifier->value(), "smooth") == 0) { + var->interpolation = ir_var_smooth; +! } else if (safe_strcmp(qualifier->value(), "flat") == 0) { + var->interpolation = ir_var_flat; +! } else if (safe_strcmp(qualifier->value(), "noperspective") == 0) { + var->interpolation = ir_var_noperspective; + } else { + ir_read_error(st, list, "unknown qualifier: %s", qualifier->value()); +*************** +*** 574,586 **** + ir_rvalue *rvalue = read_dereference(st, list); + if (rvalue != NULL || st->error) + return rvalue; +! else if (strcmp(tag->value(), "swiz") == 0) { + rvalue = read_swizzle(st, list); +! } else if (strcmp(tag->value(), "expression") == 0) { + rvalue = read_expression(st, list); +! } else if (strcmp(tag->value(), "call") == 0) { + rvalue = read_call(st, list); +! } else if (strcmp(tag->value(), "constant") == 0) { + rvalue = read_constant(st, list); + } else { + rvalue = read_texture(st, list); +--- 575,587 ---- + ir_rvalue *rvalue = read_dereference(st, list); + if (rvalue != NULL || st->error) + return rvalue; +! else if (safe_strcmp(tag->value(), "swiz") == 0) { + rvalue = read_swizzle(st, list); +! } else if (safe_strcmp(tag->value(), "expression") == 0) { + rvalue = read_expression(st, list); +! } else if (safe_strcmp(tag->value(), "call") == 0) { + rvalue = read_call(st, list); +! } else if (safe_strcmp(tag->value(), "constant") == 0) { + rvalue = read_constant(st, list); + } else { + rvalue = read_texture(st, list); +*************** +*** 921,931 **** + s_symbol *tag = SX_AS_SYMBOL(list->subexpressions.head); + assert(tag != NULL); + +! if (strcmp(tag->value(), "var_ref") == 0) + return read_var_ref(st, list); +! if (strcmp(tag->value(), "array_ref") == 0) + return read_array_ref(st, list); +! if (strcmp(tag->value(), "record_ref") == 0) + return read_record_ref(st, list); + return NULL; + } +--- 922,932 ---- + s_symbol *tag = SX_AS_SYMBOL(list->subexpressions.head); + assert(tag != NULL); + +! if (safe_strcmp(tag->value(), "var_ref") == 0) + return read_var_ref(st, list); +! if (safe_strcmp(tag->value(), "array_ref") == 0) + return read_array_ref(st, list); +! if (safe_strcmp(tag->value(), "record_ref") == 0) + return read_record_ref(st, list); + return NULL; + } +diff -c -r Mesa-7.9/src/glsl/ir_structure_splitting.cpp MesaLib/src/glsl/ir_structure_splitting.cpp +*** Mesa-7.9/src/glsl/ir_structure_splitting.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/ir_structure_splitting.cpp Fri Oct 22 15:36:52 2010 +*************** +*** 37,42 **** +--- 37,43 ---- + #include "ir_print_visitor.h" + #include "ir_rvalue_visitor.h" + #include "glsl_types.h" ++ #include "safe_strcmp.h" + + static bool debug = false; + +*************** +*** 228,235 **** + + unsigned int i; + for (i = 0; i < entry->var->type->length; i++) { +! if (strcmp(deref_record->field, +! entry->var->type->fields.structure[i].name) == 0) + break; + } + assert(i != entry->var->type->length); +--- 229,236 ---- + + unsigned int i; + for (i = 0; i < entry->var->type->length; i++) { +! if (safe_strcmp(deref_record->field, +! entry->var->type->fields.structure[i].name) == 0) + break; + } + assert(i != entry->var->type->length); +diff -c -r Mesa-7.9/src/glsl/ir_validate.cpp MesaLib/src/glsl/ir_validate.cpp +*** Mesa-7.9/src/glsl/ir_validate.cpp Mon Oct 4 18:58:00 2010 +--- MesaLib/src/glsl/ir_validate.cpp Fri Oct 22 15:37:24 2010 +*************** +*** 33,39 **** + * a dereference chain. + */ + +! #include + #include "ir.h" + #include "ir_hierarchical_visitor.h" + #include "program/hash_table.h" +--- 33,40 ---- + * a dereference chain. + */ + +! /* The use of inttypes.h seems to be unnecessary and causes problems on Windows. */ +! /* #include */ + #include "ir.h" + #include "ir_hierarchical_visitor.h" + #include "program/hash_table.h" +diff -c -r Mesa-7.9/src/glsl/linker.cpp MesaLib/src/glsl/linker.cpp +*** Mesa-7.9/src/glsl/linker.cpp Fri Oct 1 15:51:28 2010 +--- MesaLib/src/glsl/linker.cpp Fri Oct 22 15:38:08 2010 +*************** +*** 79,84 **** +--- 79,85 ---- + #include "program/hash_table.h" + #include "linker.h" + #include "ir_optimization.h" ++ #include "safe_strcmp.h" + + /** + * Visitor that determines whether or not a variable is ever written. +*************** +*** 95,101 **** + { + ir_variable *const var = ir->lhs->variable_referenced(); + +! if (strcmp(name, var->name) == 0) { + found = true; + return visit_stop; + } +--- 96,102 ---- + { + ir_variable *const var = ir->lhs->variable_referenced(); + +! if (safe_strcmp(name, var->name) == 0) { + found = true; + return visit_stop; + } +*************** +*** 113,119 **** + if (sig_param->mode == ir_var_out || + sig_param->mode == ir_var_inout) { + ir_variable *var = param_rval->variable_referenced(); +! if (var && strcmp(name, var->name) == 0) { + found = true; + return visit_stop; + } +--- 114,120 ---- + if (sig_param->mode == ir_var_out || + sig_param->mode == ir_var_inout) { + ir_variable *var = param_rval->variable_referenced(); +! if (var && safe_strcmp(name, var->name) == 0) { + found = true; + return visit_stop; + } +*************** +*** 148,154 **** + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { +! if (strcmp(this->name, ir->var->name) == 0) { + this->found = true; + return visit_stop; + } +--- 149,155 ---- + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { +! if (safe_strcmp(this->name, ir->var->name) == 0) { + this->found = true; + return visit_stop; + } +*************** +*** 876,882 **** + if (!other_var) + continue; + +! if (strcmp(var->name, other_var->name) == 0 && + other_var->max_array_access > size) { + size = other_var->max_array_access; + } +--- 877,883 ---- + if (!other_var) + continue; + +! if (safe_strcmp(var->name, other_var->name) == 0 && + other_var->max_array_access > size) { + size = other_var->max_array_access; + } +diff -c -r Mesa-7.9/src/mapi/glapi/glapi_nop.c MesaLib/src/mapi/glapi/glapi_nop.c +*** Mesa-7.9/src/mapi/glapi/glapi_nop.c Fri Oct 1 15:51:28 2010 +--- MesaLib/src/mapi/glapi/glapi_nop.c Fri Oct 22 15:41:35 2010 +*************** +*** 107,113 **** + return 0; + } + +! #define TABLE_ENTRY(name) (_glapi_proc) NoOpGeneric + + #endif + +--- 107,138 ---- + return 0; + } + +! /** +! * This is called if the user somehow calls an unassigned GL dispatch function. +! */ +! static GLint +! NoOpUnused(void) +! { +! return NoOpGeneric(); +! } +! +! /* +! * It is necessary to generate custom no-op entry points at least on +! * Windows, where the __stdcall calling convention is used (callee +! * cleans the stack). This calling convention can not tolerate a +! * mismatch between the numbers of arguments in caller and callee. +! */ +! #define KEYWORD1 static +! #define KEYWORD1_ALT static +! #define KEYWORD2 GLAPIENTRY +! #define NAME(func) NoOp##func +! #define DISPATCH(func, args, msg) NoOpGeneric(); +! #define RETURN_DISPATCH(func, args, msg) return NoOpGeneric(); +! +! /* +! * Defines for the table of no-op entry points. +! */ +! #define TABLE_ENTRY(name) (_glapi_proc) NoOp##name + + #endif + +diff -c -r Mesa-7.9/src/mesa/main/compiler.h MesaLib/src/mesa/main/compiler.h +*** Mesa-7.9/src/mesa/main/compiler.h Fri Oct 1 15:51:28 2010 +--- MesaLib/src/mesa/main/compiler.h Fri Oct 22 15:44:23 2010 +*************** +*** 175,181 **** +--- 175,183 ---- + # define PUBLIC __attribute__((visibility("default"))) + # define USED __attribute__((used)) + #else ++ # ifndef PUBLIC + # define PUBLIC ++ # endif + # define USED + #endif + +diff -c -r Mesa-7.9/src/mesa/main/histogram.c MesaLib/src/mesa/main/histogram.c +*** Mesa-7.9/src/mesa/main/histogram.c Mon Oct 4 18:58:00 2010 +--- MesaLib/src/mesa/main/histogram.c Tue Oct 26 11:17:37 2010 +*************** +*** 32,37 **** +--- 32,40 ---- + #include "macros.h" + #include "main/dispatch.h" + ++ #if defined(_MSC_VER) ++ #pragma optimize("", off) ++ #endif + + #if FEATURE_histogram + +diff -c -r Mesa-7.9/src/mesa/main/image.c MesaLib/src/mesa/main/image.c +*** Mesa-7.9/src/mesa/main/image.c Mon Oct 4 18:58:00 2010 +--- MesaLib/src/mesa/main/image.c Tue Oct 26 11:17:45 2010 +*************** +*** 37,42 **** +--- 37,45 ---- + #include "imports.h" + #include "macros.h" + ++ #if defined(_MSC_VER) ++ #pragma optimize("", off) ++ #endif + + /** + * NOTE: +diff -c -r Mesa-7.9/src/mesa/main/mipmap.c MesaLib/src/mesa/main/mipmap.c +*** Mesa-7.9/src/mesa/main/mipmap.c Fri Oct 1 15:51:28 2010 +--- MesaLib/src/mesa/main/mipmap.c Tue Oct 26 11:17:52 2010 +*************** +*** 34,40 **** + #include "texstore.h" + #include "image.h" + +! + + static GLint + bytes_per_pixel(GLenum datatype, GLuint comps) +--- 34,42 ---- + #include "texstore.h" + #include "image.h" + +! #if defined(_MSC_VER) +! #pragma optimize("", off) +! #endif + + static GLint + bytes_per_pixel(GLenum datatype, GLuint comps) +diff -c -r Mesa-7.9/src/mesa/main/querymatrix.c MesaLib/src/mesa/main/querymatrix.c +*** Mesa-7.9/src/mesa/main/querymatrix.c Mon Oct 4 18:58:00 2010 +--- MesaLib/src/mesa/main/querymatrix.c Wed Oct 6 16:36:08 2010 +*************** +*** 73,79 **** + #elif defined(__APPLE__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \ + (defined(__sun) && defined(__C99FEATURES__)) || defined(__MINGW32__) || \ +! (defined(__sun) && defined(__GNUC__)) + + /* fpclassify is available. */ + +--- 73,80 ---- + #elif defined(__APPLE__) || defined(__CYGWIN__) || defined(__FreeBSD__) || \ + defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \ + (defined(__sun) && defined(__C99FEATURES__)) || defined(__MINGW32__) || \ +! (defined(__sun) && defined(__GNUC__)) || \ +! (defined(__linux) && defined(__GNUC__)) + + /* fpclassify is available. */ + +diff -c -r Mesa-7.9/src/mesa/math/m_eval.c MesaLib/src/mesa/math/m_eval.c +*** Mesa-7.9/src/mesa/math/m_eval.c Thu Feb 4 16:10:40 2010 +--- MesaLib/src/mesa/math/m_eval.c Tue Oct 26 11:18:00 2010 +*************** +*** 41,46 **** +--- 41,50 ---- + #include "main/config.h" + #include "m_eval.h" + ++ #if defined(_MSC_VER) ++ #pragma optimize("", off) ++ #endif ++ + static GLfloat inv_tab[MAX_EVAL_ORDER]; + + +diff -c -r Mesa-7.9/src/mesa/swrast/s_texcombine.c MesaLib/src/mesa/swrast/s_texcombine.c +*** Mesa-7.9/src/mesa/swrast/s_texcombine.c Fri Oct 1 15:51:28 2010 +--- MesaLib/src/mesa/swrast/s_texcombine.c Tue Oct 26 11:01:39 2010 +*************** +*** 34,39 **** +--- 34,42 ---- + #include "s_context.h" + #include "s_texcombine.h" + ++ #if defined(_MSC_VER) ++ #pragma optimize("", off) ++ #endif + + /** + * Pointer to array of float[4] +diff -c -r Mesa-7.9/src/mapi/glapi/gen/gl_apitemp.py MesaLib/src/mapi/glapi/gen/gl_apitemp.py +*** Mesa-7.9/src/mapi/glapi/gen/gl_apitemp.py Mon Aug 15 09:14:29 2011 +--- MesaLib/src/mapi/glapi/gen/gl_apitemp.py Thu Aug 11 21:23:47 2011 +*************** +*** 79,89 **** + comma = ", " + + +- if f.return_type != 'void': +- dispatch = "RETURN_DISPATCH" +- else: +- dispatch = "DISPATCH" +- + need_proto = False + if not f.is_static_entry_point(name): + need_proto = True +--- 79,84 ---- +*************** +*** 97,108 **** + + print '%s %s KEYWORD2 NAME(%s)(%s)' % (keyword, f.return_type, n, f.get_parameter_string(name)) + print '{' +! if p_string == "": +! print ' %s(%s, (), (F, "gl%s();\\n"));' \ +! % (dispatch, f.name, name) + else: +! print ' %s(%s, (%s), (F, "gl%s(%s);\\n", %s));' \ +! % (dispatch, f.name, p_string, name, t_string, o_string) + print '}' + print '' + return +--- 92,113 ---- + + print '%s %s KEYWORD2 NAME(%s)(%s)' % (keyword, f.return_type, n, f.get_parameter_string(name)) + print '{' +! if f.return_type != 'void': +! dispatch = "RETURN_DISPATCH" +! if p_string == "": +! print ' %s(%s, %s, (), (F, "gl%s();\\n"));' \ +! % (dispatch, f.return_type, f.name, name) +! else: +! print ' %s(%s, %s, (%s), (F, "gl%s(%s);\\n", %s));' \ +! % (dispatch, f.return_type, f.name, p_string, name, t_string, o_string) + else: +! dispatch = "DISPATCH" +! if p_string == "": +! print ' %s(%s, (), (F, "gl%s();\\n"));' \ +! % (dispatch, f.name, name) +! else: +! print ' %s(%s, (%s), (F, "gl%s(%s);\\n", %s));' \ +! % (dispatch, f.name, p_string, name, t_string, o_string) + print '}' + print '' + return +*************** +*** 120,126 **** + * NAME(n) - builds the final function name (usually add "gl" prefix) + * DISPATCH(func, args, msg) - code to do dispatch of named function. + * msg is a printf-style debug message. +! * RETURN_DISPATCH(func, args, msg) - code to do dispatch with a return value + * + * Here is an example which generates the usual OpenGL functions: + * #define KEYWORD1 +--- 125,132 ---- + * NAME(n) - builds the final function name (usually add "gl" prefix) + * DISPATCH(func, args, msg) - code to do dispatch of named function. + * msg is a printf-style debug message. +! * RETURN_DISPATCH(type, func, args, msg) - code to do dispatch with a +! * return value of type. + * + * Here is an example which generates the usual OpenGL functions: + * #define KEYWORD1 +diff -c -r Mesa-7.9/src/mapi/glapi/glapi_dispatch.c MesaLib/src/mapi/glapi/glapi_dispatch.c +*** Mesa-7.9/src/mapi/glapi/glapi_dispatch.c Mon Aug 15 09:14:30 2011 +--- MesaLib/src/mapi/glapi/glapi_dispatch.c Thu Aug 11 21:25:55 2011 +*************** +*** 65,71 **** + fprintf MESSAGE; \ + CALL_ ## FUNC(GET_DISPATCH(), ARGS); + +! #define RETURN_DISPATCH(FUNC, ARGS, MESSAGE) \ + fprintf MESSAGE; \ + return CALL_ ## FUNC(GET_DISPATCH(), ARGS); + +--- 65,71 ---- + fprintf MESSAGE; \ + CALL_ ## FUNC(GET_DISPATCH(), ARGS); + +! #define RETURN_DISPATCH(TYPE, FUNC, ARGS, MESSAGE) \ + fprintf MESSAGE; \ + return CALL_ ## FUNC(GET_DISPATCH(), ARGS); + +*************** +*** 74,80 **** + #define DISPATCH(FUNC, ARGS, MESSAGE) \ + CALL_ ## FUNC(GET_DISPATCH(), ARGS); + +! #define RETURN_DISPATCH(FUNC, ARGS, MESSAGE) \ + return CALL_ ## FUNC(GET_DISPATCH(), ARGS); + + #endif /* logging */ +--- 74,80 ---- + #define DISPATCH(FUNC, ARGS, MESSAGE) \ + CALL_ ## FUNC(GET_DISPATCH(), ARGS); + +! #define RETURN_DISPATCH(TYPE, FUNC, ARGS, MESSAGE) \ + return CALL_ ## FUNC(GET_DISPATCH(), ARGS); + + #endif /* logging */ +diff -c -r Mesa-7.9/src/mapi/glapi/glapi_nop.c MesaLib/src/mapi/glapi/glapi_nop.c +*** Mesa-7.9/src/mapi/glapi/glapi_nop.c Mon Aug 15 09:14:30 2011 +--- MesaLib/src/mapi/glapi/glapi_nop.c Thu Aug 11 21:29:46 2011 +*************** +*** 86,92 **** + #define KEYWORD2 GLAPIENTRY + #define NAME(func) NoOp##func + #define DISPATCH(func, args, msg) Warn(#func); +! #define RETURN_DISPATCH(func, args, msg) Warn(#func); return 0 + + + /* +--- 86,92 ---- + #define KEYWORD2 GLAPIENTRY + #define NAME(func) NoOp##func + #define DISPATCH(func, args, msg) Warn(#func); +! #define RETURN_DISPATCH(type, func, args, msg) Warn(#func); return (type)0 + + + /* +*************** +*** 96,102 **** + + #else + +! static int + NoOpGeneric(void) + { + #if !defined(_WIN32_WCE) +--- 96,102 ---- + + #else + +! void + NoOpGeneric(void) + { + #if !defined(_WIN32_WCE) +*************** +*** 104,110 **** + fprintf(stderr, "GL User Error: calling GL function without a rendering context\n"); + } + #endif +- return 0; + } + + /** +--- 104,109 ---- +*************** +*** 113,119 **** + static GLint + NoOpUnused(void) + { +! return NoOpGeneric(); + } + + /* +--- 112,119 ---- + static GLint + NoOpUnused(void) + { +! NoOpGeneric(); +! return 0; + } + + /* +*************** +*** 127,133 **** + #define KEYWORD2 GLAPIENTRY + #define NAME(func) NoOp##func + #define DISPATCH(func, args, msg) NoOpGeneric(); +! #define RETURN_DISPATCH(func, args, msg) return NoOpGeneric(); + + /* + * Defines for the table of no-op entry points. +--- 127,133 ---- + #define KEYWORD2 GLAPIENTRY + #define NAME(func) NoOp##func + #define DISPATCH(func, args, msg) NoOpGeneric(); +! #define RETURN_DISPATCH(type, func, args, msg) NoOpGeneric(); return (type)0 + + /* + * Defines for the table of no-op entry points. diff --git a/engine/src/flutter/third_party/mesa/generate_git_sha1.py b/engine/src/flutter/third_party/mesa/generate_git_sha1.py new file mode 100644 index 0000000000..5e41c852ab --- /dev/null +++ b/engine/src/flutter/third_party/mesa/generate_git_sha1.py @@ -0,0 +1,35 @@ +# 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. + +import os +import os.path +import sys + +output = sys.argv[1] +parentdir = os.path.abspath(os.path.join(output, os.pardir)) + +#The original body of this file is generated by this bash script: +# +#touch "${DIR}/git_sha1.h.tmp" +#if test -d .git; then \ +# if which git > /dev/null; then \ +# git log -n 1 --oneline | \ +# sed 's/^\([^ ]*\) .*/#define MESA_GIT_SHA1 "git-\1"/' \ +# > "${DIR}/git_sha1.h.tmp" ; \ +# fi \ +# fi +#if ! cmp -s "${DIR}/git_sha1.h.tmp" "${DIR}/git_sha1.h"; then \ +# mv "${DIR}/git_sha1.h.tmp" "${DIR}/git_sha1.h" ;\ +# else \ +# rm "${DIR}/git_sha1.h.tmp" ;\ +# fi +# +#However, Chromium shouldn't depend on Bash, and this preprocessor macro isn't +#neccessary in the first place + +if not os.path.isdir(parentdir): + os.makedirs(parentdir) + +with open(output, "w") as f: + pass diff --git a/engine/src/flutter/third_party/mesa/mesa.gyp b/engine/src/flutter/third_party/mesa/mesa.gyp new file mode 100644 index 0000000000..82027b35e7 --- /dev/null +++ b/engine/src/flutter/third_party/mesa/mesa.gyp @@ -0,0 +1,750 @@ +# Copyright (c) 2012 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. + +{ + 'variables': { + 'generated_src_dir': 'src/chromium_gensrc', + }, + 'target_defaults': { + 'defines': [ + 'MAPI_ABI_HEADER="glapi_mapi_tmp_shared.h"', + "PACKAGE_NAME=\"Mesa\"", + "PACKAGE_TARNAME=\"mesa\"", + "PACKAGE_VERSION=\"9.0.3\"", + "PACKAGE_STRING=\"Mesa\ 9.0.3\"", + "PACKAGE_BUGREPORT=\"https://bugs.freedesktop.org/enter_bug.cgi\?product=Mesa\"", + "PACKAGE_URL=\"\"", + "PACKAGE=\"mesa\"", + "VERSION=\"9.0.3\"", + "STDC_HEADERS=1", + "HAVE_SYS_TYPES_H=1", + "HAVE_SYS_STAT_H=1", + "HAVE_STDLIB_H=1", + "HAVE_STRING_H=1", + "HAVE_MEMORY_H=1", + "HAVE_STRINGS_H=1", + "HAVE_INTTYPES_H=1", + "HAVE_STDINT_H=1", + "HAVE_DLFCN_H=1", + "LT_OBJDIR=\".libs/\"", + "YYTEXT_POINTER=1", + "HAVE_LIBEXPAT=1", + "HAVE_LIBXCB_DRI2=1", + "FEATURE_GL=1", + 'MAPI_MODE_GLAPI', + #"USE_X86_64_ASM", + "IN_DRI_DRIVER", + "USE_XCB", + "GLX_INDIRECT_RENDERING", + "GLX_DIRECT_RENDERING", + "USE_EXTERNAL_DXTN_LIB=1", + "IN_DRI_DRIVER", + "HAVE_ALIAS", + "HAVE_MINCORE", + "HAVE_LIBUDEV", + "_GLAPI_NO_EXPORTS", + ], + 'conditions': [ + ['OS=="android"', { + 'defines': [ + '__GLIBC__', + '_GNU_SOURCE', + ], + }], + ['OS=="linux"', { + 'defines': [ + '_GNU_SOURCE', + ], + }], + ['os_posix == 1', { + 'defines': [ + 'HAVE_DLOPEN', + 'HAVE_PTHREAD=1', + 'HAVE_UNISTD_H=1', + ], + }], + ['os_posix == 1 and OS != "android"', { + 'defines': [ + 'HAVE_POSIX_MEMALIGN', + ], + }], + ['os_posix == 1 and OS != "mac" and OS != "android"', { + 'cflags': [ + '-fPIC', + ], + }], + ['ubsan_vptr == 1', { + 'cflags!': [ + # UBsan's vptr is not compatible with -fno-rtti, + # which is used by gallium/auxiliary/Makefile. + '-fsanitize=null', + '-fsanitize=vptr', + '-fsanitize-coverage=<(sanitizer_coverage)', + ], + }], + ], + }, + 'targets': [ + { + 'target_name': 'mesa_headers', + 'type': 'none', + 'direct_dependent_settings': { + 'include_dirs': [ + 'src/include', + ], + }, + 'conditions': [ + ['use_x11==0', { + 'direct_dependent_settings': { + 'defines': [ + 'MESA_EGL_NO_X11_HEADERS', + ], + }, + }], + ], + }, + { + 'target_name': 'mesa_libglslcommon', + 'type': 'static_library', + 'include_dirs': [ + 'src/src/gallium/auxiliary', + 'src/src/gallium/include', + 'src/src/glsl', + 'src/src/glsl/glcpp', + 'src/src/mapi', + 'src/src/mapi/glapi', + 'src/src/mesa', + 'src/src/mesa/main', + 'src/include', + '<(generated_src_dir)/mesa/', + '<(generated_src_dir)/mesa/main', + '<(generated_src_dir)/mesa/program', + '<(generated_src_dir)/mesa/glapi', + ], + 'dependencies': [ + 'mesa_headers', + ], + # TODO(scottmg): http://crbug.com/143877 These should be removed if + # Mesa is ever rolled and the warnings are fixed. + 'msvs_disabled_warnings': [ + 4005, 4018, 4065, 4090, 4099, 4291, 4345, 4267, + ], + 'variables': { + 'clang_warning_flags': [ + '-Wno-tautological-constant-out-of-range-compare', + '-Wno-mismatched-tags', # Fixed upstream. + ], + 'clang_warning_flags_unset': [ + # Don't warn about string->bool used in asserts. + '-Wstring-conversion', + ], + }, + 'sources': [ + '<(generated_src_dir)/mesa/main/dispatch.h', + 'src/src/glsl/ast_expr.cpp', + 'src/src/glsl/ast_function.cpp', + 'src/src/glsl/ast_to_hir.cpp', + 'src/src/glsl/ast_type.cpp', + 'src/src/glsl/builtin_variables.cpp', + '<(generated_src_dir)/mesa/glcpp-lex.c', + '<(generated_src_dir)/mesa/glcpp-parse.c', + '<(generated_src_dir)/mesa/glcpp-parse.h', + 'src/src/glsl/glcpp/glcpp.h', + 'src/src/glsl/glcpp/pp.c', + '<(generated_src_dir)/mesa/glsl_lexer.cc', + '<(generated_src_dir)/mesa/glsl_parser.cc', + 'src/src/glsl/glsl_parser_extras.cpp', + 'src/src/glsl/glsl_parser_extras.h', + 'src/src/glsl/glsl_symbol_table.cpp', + 'src/src/glsl/glsl_symbol_table.h', + 'src/src/glsl/glsl_types.cpp', + 'src/src/glsl/glsl_types.h', + 'src/src/glsl/hir_field_selection.cpp', + 'src/src/glsl/ir.cpp', + 'src/src/glsl/ir.h', + 'src/src/glsl/ir_basic_block.cpp', + 'src/src/glsl/ir_basic_block.h', + 'src/src/glsl/ir_builder.cpp', + 'src/src/glsl/ir_builder.h', + 'src/src/glsl/ir_clone.cpp', + 'src/src/glsl/ir_constant_expression.cpp', + 'src/src/glsl/ir_expression_flattening.cpp', + 'src/src/glsl/ir_expression_flattening.h', + 'src/src/glsl/ir_function.cpp', + 'src/src/glsl/ir_function_can_inline.cpp', + 'src/src/glsl/ir_function_detect_recursion.cpp', + 'src/src/glsl/ir_hierarchical_visitor.cpp', + 'src/src/glsl/ir_hierarchical_visitor.h', + 'src/src/glsl/ir_hv_accept.cpp', + 'src/src/glsl/ir_import_prototypes.cpp', + 'src/src/glsl/ir_print_visitor.cpp', + 'src/src/glsl/ir_print_visitor.h', + 'src/src/glsl/ir_reader.cpp', + 'src/src/glsl/ir_reader.h', + 'src/src/glsl/ir_rvalue_visitor.cpp', + 'src/src/glsl/ir_rvalue_visitor.h', + 'src/src/glsl/ir_set_program_inouts.cpp', + 'src/src/glsl/ir_validate.cpp', + 'src/src/glsl/ir_variable_refcount.cpp', + 'src/src/glsl/ir_variable_refcount.h', + 'src/src/glsl/link_functions.cpp', + 'src/src/glsl/link_uniform_initializers.cpp', + 'src/src/glsl/link_uniforms.cpp', + 'src/src/glsl/linker.cpp', + 'src/src/glsl/linker.h', + 'src/src/glsl/loop_analysis.cpp', + 'src/src/glsl/loop_analysis.h', + 'src/src/glsl/loop_controls.cpp', + 'src/src/glsl/loop_unroll.cpp', + 'src/src/glsl/lower_clip_distance.cpp', + 'src/src/glsl/lower_discard.cpp', + 'src/src/glsl/lower_discard_flow.cpp', + 'src/src/glsl/lower_if_to_cond_assign.cpp', + 'src/src/glsl/lower_instructions.cpp', + 'src/src/glsl/lower_jumps.cpp', + 'src/src/glsl/lower_mat_op_to_vec.cpp', + 'src/src/glsl/lower_noise.cpp', + 'src/src/glsl/lower_output_reads.cpp', + 'src/src/glsl/lower_texture_projection.cpp', + 'src/src/glsl/lower_ubo_reference.cpp', + 'src/src/glsl/lower_variable_index_to_cond_assign.cpp', + 'src/src/glsl/lower_vec_index_to_cond_assign.cpp', + 'src/src/glsl/lower_vec_index_to_swizzle.cpp', + 'src/src/glsl/lower_vector.cpp', + 'src/src/glsl/opt_algebraic.cpp', + 'src/src/glsl/opt_array_splitting.cpp', + 'src/src/glsl/opt_constant_folding.cpp', + 'src/src/glsl/opt_constant_propagation.cpp', + 'src/src/glsl/opt_constant_variable.cpp', + 'src/src/glsl/opt_copy_propagation.cpp', + 'src/src/glsl/opt_copy_propagation_elements.cpp', + 'src/src/glsl/opt_dead_code.cpp', + 'src/src/glsl/opt_dead_code_local.cpp', + 'src/src/glsl/opt_dead_functions.cpp', + 'src/src/glsl/opt_function_inlining.cpp', + 'src/src/glsl/opt_if_simplification.cpp', + 'src/src/glsl/opt_noop_swizzle.cpp', + 'src/src/glsl/opt_redundant_jumps.cpp', + 'src/src/glsl/opt_structure_splitting.cpp', + 'src/src/glsl/opt_swizzle_swizzle.cpp', + 'src/src/glsl/opt_tree_grafting.cpp', + 'src/src/glsl/program.h', + 'src/src/glsl/ralloc.c', + 'src/src/glsl/ralloc.h', + 'src/src/glsl/s_expression.cpp', + 'src/src/glsl/s_expression.h', + # This file is not needed and has duplicate symbols (although it + # happens to link because of static library link ordering). + #'src/src/glsl/standalone_scaffolding.cpp', + #'src/src/glsl/standalone_scaffolding.h', + 'src/src/glsl/strtod.c', + 'src/src/glsl/strtod.h', + ], + }, + { + 'target_name': 'mesa', + 'type': 'static_library', + 'include_dirs': [ + 'src/src/gallium/auxiliary', + 'src/src/gallium/include', + 'src/src/glsl', + 'src/src/glsl/glcpp', + 'src/src/mapi', + 'src/src/mapi/glapi', + 'src/src/mesa', + 'src/src/mesa/main', + '<(generated_src_dir)/mesa/', + '<(generated_src_dir)/mesa/main', + '<(generated_src_dir)/mesa/program', + '<(generated_src_dir)/mesa/glapi', + ], + 'dependencies': [ + 'mesa_headers', + 'mesa_libglslcommon', + ], + # TODO(scottmg): http://crbug.com/143877 These should be removed if + # Mesa is ever rolled and the warnings are fixed. + 'msvs_disabled_warnings': [ + 4005, 4018, 4090, 4099, 4146, 4291, 4305, 4334, 4748, 4267, + ], + 'variables': { + 'clang_warning_flags': [ + '-Wno-tautological-constant-out-of-range-compare', + '-Wno-absolute-value', # Fires on st_atom_array.c, might be a bug + '-Wno-mismatched-tags', # Fixed upstream. + ], + 'clang_warning_flags_unset': [ + # Don't warn about string->bool used in asserts. + '-Wstring-conversion', + ], + }, + 'sources': [ + '<(generated_src_dir)/mesa/builtin_function.cpp', + '<(generated_src_dir)/mesa/glapi_mapi_tmp_shared.h', + 'src/src/mapi/mapi/entry.c', + 'src/src/mapi/mapi/entry.h', + 'src/src/mapi/mapi/mapi_glapi.c', + 'src/src/mapi/mapi/stub.c', + 'src/src/mapi/mapi/stub.h', + 'src/src/mapi/mapi/table.c', + 'src/src/mapi/mapi/table.h', + 'src/src/mapi/mapi/u_current.c', + 'src/src/mapi/mapi/u_current.h', + 'src/src/mapi/mapi/u_execmem.c', + 'src/src/mapi/mapi/u_execmem.h', + 'src/src/mesa/main/accum.c', + 'src/src/mesa/main/accum.h', + 'src/src/mesa/main/api_arrayelt.c', + 'src/src/mesa/main/api_arrayelt.h', + 'src/src/mesa/main/api_exec.c', + 'src/src/mesa/main/api_exec.h', + 'src/src/mesa/main/api_loopback.c', + 'src/src/mesa/main/api_loopback.h', + 'src/src/mesa/main/api_validate.c', + 'src/src/mesa/main/api_validate.h', + 'src/src/mesa/main/arbprogram.c', + 'src/src/mesa/main/arbprogram.h', + 'src/src/mesa/main/arrayobj.c', + 'src/src/mesa/main/arrayobj.h', + 'src/src/mesa/main/atifragshader.c', + 'src/src/mesa/main/atifragshader.h', + 'src/src/mesa/main/attrib.c', + 'src/src/mesa/main/attrib.h', + 'src/src/mesa/main/blend.c', + 'src/src/mesa/main/blend.h', + 'src/src/mesa/main/bufferobj.c', + 'src/src/mesa/main/bufferobj.h', + 'src/src/mesa/main/buffers.c', + 'src/src/mesa/main/buffers.h', + 'src/src/mesa/main/clear.c', + 'src/src/mesa/main/clear.h', + 'src/src/mesa/main/clip.c', + 'src/src/mesa/main/clip.h', + 'src/src/mesa/main/colortab.c', + 'src/src/mesa/main/colortab.h', + 'src/src/mesa/main/condrender.c', + 'src/src/mesa/main/condrender.h', + 'src/src/mesa/main/context.c', + 'src/src/mesa/main/context.h', + 'src/src/mesa/main/convolve.c', + 'src/src/mesa/main/convolve.h', + 'src/src/mesa/main/cpuinfo.c', + 'src/src/mesa/main/cpuinfo.h', + 'src/src/mesa/main/debug.c', + 'src/src/mesa/main/debug.h', + 'src/src/mesa/main/depth.c', + 'src/src/mesa/main/depth.h', + 'src/src/mesa/main/dlist.c', + 'src/src/mesa/main/dlist.h', + 'src/src/mesa/main/drawpix.c', + 'src/src/mesa/main/drawpix.h', + 'src/src/mesa/main/drawtex.c', + 'src/src/mesa/main/drawtex.h', + 'src/src/mesa/main/enable.c', + 'src/src/mesa/main/enable.h', + '<(generated_src_dir)/mesa/enums.c', + 'src/src/mesa/main/enums.h', + 'src/src/mesa/main/errors.c', + 'src/src/mesa/main/errors.h', + 'src/src/mesa/main/eval.c', + 'src/src/mesa/main/eval.h', + 'src/src/mesa/main/execmem.c', + 'src/src/mesa/main/extensions.c', + 'src/src/mesa/main/extensions.h', + 'src/src/mesa/main/fbobject.c', + 'src/src/mesa/main/fbobject.h', + 'src/src/mesa/main/feedback.c', + 'src/src/mesa/main/feedback.h', + 'src/src/mesa/main/ff_fragment_shader.cpp', + 'src/src/mesa/main/ffvertex_prog.c', + 'src/src/mesa/main/ffvertex_prog.h', + 'src/src/mesa/main/fog.c', + 'src/src/mesa/main/fog.h', + 'src/src/mesa/main/format_pack.c', + 'src/src/mesa/main/format_pack.h', + 'src/src/mesa/main/format_unpack.c', + 'src/src/mesa/main/format_unpack.h', + 'src/src/mesa/main/formats.c', + 'src/src/mesa/main/formats.h', + 'src/src/mesa/main/framebuffer.c', + 'src/src/mesa/main/framebuffer.h', + 'src/src/mesa/main/get.c', + 'src/src/mesa/main/get.h', + 'src/src/mesa/main/getstring.c', + 'src/src/mesa/main/glformats.c', + 'src/src/mesa/main/glformats.h', + 'src/src/mesa/main/hash.c', + 'src/src/mesa/main/hash.h', + 'src/src/mesa/main/hint.c', + 'src/src/mesa/main/hint.h', + 'src/src/mesa/main/histogram.c', + 'src/src/mesa/main/histogram.h', + 'src/src/mesa/main/image.c', + 'src/src/mesa/main/image.h', + 'src/src/mesa/main/imports.c', + 'src/src/mesa/main/imports.h', + 'src/src/mesa/main/light.c', + 'src/src/mesa/main/light.h', + 'src/src/mesa/main/lines.c', + 'src/src/mesa/main/lines.h', + 'src/src/mesa/main/matrix.c', + 'src/src/mesa/main/matrix.h', + 'src/src/mesa/main/mipmap.c', + 'src/src/mesa/main/mipmap.h', + 'src/src/mesa/main/mm.c', + 'src/src/mesa/main/mm.h', + 'src/src/mesa/main/multisample.c', + 'src/src/mesa/main/multisample.h', + 'src/src/mesa/main/nvprogram.c', + 'src/src/mesa/main/nvprogram.h', + 'src/src/mesa/main/pack.c', + 'src/src/mesa/main/pack.h', + 'src/src/mesa/main/pbo.c', + 'src/src/mesa/main/pbo.h', + 'src/src/mesa/main/pixel.c', + 'src/src/mesa/main/pixel.h', + 'src/src/mesa/main/pixelstore.c', + 'src/src/mesa/main/pixelstore.h', + 'src/src/mesa/main/pixeltransfer.c', + 'src/src/mesa/main/pixeltransfer.h', + 'src/src/mesa/main/points.c', + 'src/src/mesa/main/points.h', + 'src/src/mesa/main/polygon.c', + 'src/src/mesa/main/polygon.h', + 'src/src/mesa/main/queryobj.c', + 'src/src/mesa/main/queryobj.h', + 'src/src/mesa/main/rastpos.c', + 'src/src/mesa/main/rastpos.h', + 'src/src/mesa/main/readpix.c', + 'src/src/mesa/main/readpix.h', + 'src/src/mesa/main/remap.c', + 'src/src/mesa/main/remap.h', + 'src/src/mesa/main/renderbuffer.c', + 'src/src/mesa/main/renderbuffer.h', + 'src/src/mesa/main/samplerobj.c', + 'src/src/mesa/main/samplerobj.h', + 'src/src/mesa/main/scissor.c', + 'src/src/mesa/main/scissor.h', + 'src/src/mesa/main/shader_query.cpp', + 'src/src/mesa/main/shaderapi.c', + 'src/src/mesa/main/shaderapi.h', + 'src/src/mesa/main/shaderobj.c', + 'src/src/mesa/main/shaderobj.h', + 'src/src/mesa/main/shared.c', + 'src/src/mesa/main/shared.h', + 'src/src/mesa/main/state.c', + 'src/src/mesa/main/state.h', + 'src/src/mesa/main/stencil.c', + 'src/src/mesa/main/stencil.h', + 'src/src/mesa/main/syncobj.c', + 'src/src/mesa/main/syncobj.h', + 'src/src/mesa/main/texcompress.c', + 'src/src/mesa/main/texcompress.h', + 'src/src/mesa/main/texcompress_cpal.c', + 'src/src/mesa/main/texcompress_cpal.h', + 'src/src/mesa/main/texcompress_etc.c', + 'src/src/mesa/main/texcompress_etc.h', + 'src/src/mesa/main/texcompress_fxt1.c', + 'src/src/mesa/main/texcompress_fxt1.h', + 'src/src/mesa/main/texcompress_rgtc.c', + 'src/src/mesa/main/texcompress_rgtc.h', + 'src/src/mesa/main/texcompress_s3tc.c', + 'src/src/mesa/main/texcompress_s3tc.h', + 'src/src/mesa/main/texenv.c', + 'src/src/mesa/main/texenv.h', + 'src/src/mesa/main/texformat.c', + 'src/src/mesa/main/texformat.h', + 'src/src/mesa/main/texgen.c', + 'src/src/mesa/main/texgen.h', + 'src/src/mesa/main/texgetimage.c', + 'src/src/mesa/main/texgetimage.h', + 'src/src/mesa/main/teximage.c', + 'src/src/mesa/main/teximage.h', + 'src/src/mesa/main/texobj.c', + 'src/src/mesa/main/texobj.h', + 'src/src/mesa/main/texparam.c', + 'src/src/mesa/main/texparam.h', + 'src/src/mesa/main/texstate.c', + 'src/src/mesa/main/texstate.h', + 'src/src/mesa/main/texstorage.c', + 'src/src/mesa/main/texstorage.h', + 'src/src/mesa/main/texstore.c', + 'src/src/mesa/main/texstore.h', + 'src/src/mesa/main/texturebarrier.c', + 'src/src/mesa/main/texturebarrier.h', + 'src/src/mesa/main/transformfeedback.c', + 'src/src/mesa/main/transformfeedback.h', + 'src/src/mesa/main/uniform_query.cpp', + 'src/src/mesa/main/uniforms.c', + 'src/src/mesa/main/uniforms.h', + 'src/src/mesa/main/varray.c', + 'src/src/mesa/main/varray.h', + 'src/src/mesa/main/version.c', + 'src/src/mesa/main/version.h', + 'src/src/mesa/main/viewport.c', + 'src/src/mesa/main/viewport.h', + 'src/src/mesa/main/vtxfmt.c', + 'src/src/mesa/main/vtxfmt.h', + 'src/src/mesa/math/m_debug_clip.c', + 'src/src/mesa/math/m_debug_norm.c', + 'src/src/mesa/math/m_debug_xform.c', + 'src/src/mesa/math/m_eval.c', + 'src/src/mesa/math/m_eval.h', + 'src/src/mesa/math/m_matrix.c', + 'src/src/mesa/math/m_matrix.h', + 'src/src/mesa/math/m_translate.c', + 'src/src/mesa/math/m_translate.h', + 'src/src/mesa/math/m_vector.c', + 'src/src/mesa/math/m_vector.h', + 'src/src/mesa/math/m_xform.c', + 'src/src/mesa/math/m_xform.h', + 'src/src/mesa/program/arbprogparse.c', + 'src/src/mesa/program/arbprogparse.h', + 'src/src/mesa/program/hash_table.c', + 'src/src/mesa/program/hash_table.h', + 'src/src/mesa/program/ir_to_mesa.cpp', + 'src/src/mesa/program/ir_to_mesa.h', + '<(generated_src_dir)/mesa/lex.yy.c', + 'src/src/mesa/program/nvfragparse.c', + 'src/src/mesa/program/nvfragparse.h', + 'src/src/mesa/program/nvvertparse.c', + 'src/src/mesa/program/nvvertparse.h', + 'src/src/mesa/program/prog_cache.c', + 'src/src/mesa/program/prog_cache.h', + 'src/src/mesa/program/prog_execute.c', + 'src/src/mesa/program/prog_execute.h', + 'src/src/mesa/program/prog_instruction.c', + 'src/src/mesa/program/prog_instruction.h', + 'src/src/mesa/program/prog_noise.c', + 'src/src/mesa/program/prog_noise.h', + 'src/src/mesa/program/prog_opt_constant_fold.c', + 'src/src/mesa/program/prog_optimize.c', + 'src/src/mesa/program/prog_optimize.h', + 'src/src/mesa/program/prog_parameter.c', + 'src/src/mesa/program/prog_parameter.h', + 'src/src/mesa/program/prog_parameter_layout.c', + 'src/src/mesa/program/prog_parameter_layout.h', + 'src/src/mesa/program/prog_print.c', + 'src/src/mesa/program/prog_print.h', + 'src/src/mesa/program/prog_statevars.c', + 'src/src/mesa/program/prog_statevars.h', + 'src/src/mesa/program/program.c', + 'src/src/mesa/program/program.h', + '<(generated_src_dir)/mesa/program/program_parse.tab.c', + '<(generated_src_dir)/mesa/program/program_parse.tab.h', + 'src/src/mesa/program/program_parse_extra.c', + 'src/src/mesa/program/programopt.c', + 'src/src/mesa/program/programopt.h', + 'src/src/mesa/program/register_allocate.c', + 'src/src/mesa/program/register_allocate.h', + 'src/src/mesa/program/sampler.cpp', + 'src/src/mesa/program/sampler.h', + 'src/src/mesa/program/string_to_uint_map.cpp', + 'src/src/mesa/program/symbol_table.c', + 'src/src/mesa/program/symbol_table.h', + 'src/src/mesa/swrast/s_aaline.c', + 'src/src/mesa/swrast/s_aaline.h', + 'src/src/mesa/swrast/s_aatriangle.c', + 'src/src/mesa/swrast/s_aatriangle.h', + 'src/src/mesa/swrast/s_alpha.c', + 'src/src/mesa/swrast/s_alpha.h', + 'src/src/mesa/swrast/s_atifragshader.c', + 'src/src/mesa/swrast/s_atifragshader.h', + 'src/src/mesa/swrast/s_bitmap.c', + 'src/src/mesa/swrast/s_blend.c', + 'src/src/mesa/swrast/s_blend.h', + 'src/src/mesa/swrast/s_blit.c', + 'src/src/mesa/swrast/s_clear.c', + 'src/src/mesa/swrast/s_context.c', + 'src/src/mesa/swrast/s_context.h', + 'src/src/mesa/swrast/s_copypix.c', + 'src/src/mesa/swrast/s_depth.c', + 'src/src/mesa/swrast/s_depth.h', + 'src/src/mesa/swrast/s_drawpix.c', + 'src/src/mesa/swrast/s_feedback.c', + 'src/src/mesa/swrast/s_feedback.h', + 'src/src/mesa/swrast/s_fog.c', + 'src/src/mesa/swrast/s_fog.h', + 'src/src/mesa/swrast/s_fragprog.c', + 'src/src/mesa/swrast/s_fragprog.h', + 'src/src/mesa/swrast/s_lines.c', + 'src/src/mesa/swrast/s_lines.h', + 'src/src/mesa/swrast/s_logic.c', + 'src/src/mesa/swrast/s_logic.h', + 'src/src/mesa/swrast/s_masking.c', + 'src/src/mesa/swrast/s_masking.h', + 'src/src/mesa/swrast/s_points.c', + 'src/src/mesa/swrast/s_points.h', + 'src/src/mesa/swrast/s_renderbuffer.c', + 'src/src/mesa/swrast/s_renderbuffer.h', + 'src/src/mesa/swrast/s_span.c', + 'src/src/mesa/swrast/s_span.h', + 'src/src/mesa/swrast/s_stencil.c', + 'src/src/mesa/swrast/s_stencil.h', + 'src/src/mesa/swrast/s_texcombine.c', + 'src/src/mesa/swrast/s_texcombine.h', + 'src/src/mesa/swrast/s_texfetch.c', + 'src/src/mesa/swrast/s_texfetch.h', + 'src/src/mesa/swrast/s_texfilter.c', + 'src/src/mesa/swrast/s_texfilter.h', + 'src/src/mesa/swrast/s_texrender.c', + 'src/src/mesa/swrast/s_texture.c', + 'src/src/mesa/swrast/s_triangle.c', + 'src/src/mesa/swrast/s_triangle.h', + 'src/src/mesa/swrast/s_zoom.c', + 'src/src/mesa/swrast/s_zoom.h', + 'src/src/mesa/swrast_setup/ss_context.c', + 'src/src/mesa/swrast_setup/ss_context.h', + 'src/src/mesa/swrast_setup/ss_triangle.c', + 'src/src/mesa/swrast_setup/ss_triangle.h', + 'src/src/mesa/tnl/t_context.c', + 'src/src/mesa/tnl/t_context.h', + 'src/src/mesa/tnl/t_draw.c', + 'src/src/mesa/tnl/t_pipeline.c', + 'src/src/mesa/tnl/t_pipeline.h', + 'src/src/mesa/tnl/t_rasterpos.c', + 'src/src/mesa/tnl/t_vb_fog.c', + 'src/src/mesa/tnl/t_vb_light.c', + 'src/src/mesa/tnl/t_vb_normals.c', + 'src/src/mesa/tnl/t_vb_points.c', + 'src/src/mesa/tnl/t_vb_program.c', + 'src/src/mesa/tnl/t_vb_render.c', + 'src/src/mesa/tnl/t_vb_texgen.c', + 'src/src/mesa/tnl/t_vb_texmat.c', + 'src/src/mesa/tnl/t_vb_vertex.c', + 'src/src/mesa/tnl/t_vertex.c', + 'src/src/mesa/tnl/t_vertex.h', + 'src/src/mesa/tnl/t_vertex_generic.c', + 'src/src/mesa/tnl/t_vertex_sse.c', + 'src/src/mesa/tnl/t_vp_build.c', + 'src/src/mesa/tnl/t_vp_build.h', + 'src/src/mesa/vbo/vbo_context.c', + 'src/src/mesa/vbo/vbo_context.h', + 'src/src/mesa/vbo/vbo_exec.c', + 'src/src/mesa/vbo/vbo_exec.h', + 'src/src/mesa/vbo/vbo_exec_api.c', + 'src/src/mesa/vbo/vbo_exec_array.c', + 'src/src/mesa/vbo/vbo_exec_draw.c', + 'src/src/mesa/vbo/vbo_exec_eval.c', + 'src/src/mesa/vbo/vbo_noop.c', + 'src/src/mesa/vbo/vbo_noop.h', + 'src/src/mesa/vbo/vbo_primitive_restart.c', + 'src/src/mesa/vbo/vbo_rebase.c', + 'src/src/mesa/vbo/vbo_save.c', + 'src/src/mesa/vbo/vbo_save.h', + 'src/src/mesa/vbo/vbo_save_api.c', + 'src/src/mesa/vbo/vbo_save_draw.c', + 'src/src/mesa/vbo/vbo_save_loopback.c', + 'src/src/mesa/vbo/vbo_split.c', + 'src/src/mesa/vbo/vbo_split.h', + 'src/src/mesa/vbo/vbo_split_copy.c', + 'src/src/mesa/vbo/vbo_split_inplace.c', + 'src/src/mesa/x86-64/x86-64.c', + 'src/src/mesa/x86-64/x86-64.h', + ], + 'conditions': [ + ['OS=="android" and clang==0', { + # Disable sincos() optimization to avoid a linker error + # since Android's math library doesn't have sincos(). + # Either -fno-builtin-sin or -fno-builtin-cos works. + 'cflags': [ + '-fno-builtin-sin', + ], + }], + ['OS=="win"', { + 'defines': [ + # Because we're building as a static library + '_GLAPI_NO_EXPORTS', + ], + }], + ], + }, + # Building this target will hide the native OpenGL shared library and + # replace it with a slow software renderer. + { + 'target_name': 'osmesa', + 'type': 'loadable_module', + 'mac_bundle': 0, + 'dependencies': [ + 'mesa_headers', + 'mesa', + ], + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-lstdc++', + ], + }, + 'conditions': [ + ['OS=="win"', { + 'defines': [ + 'BUILD_GL32', + 'KEYWORD1=GLAPI', + 'KEYWORD2=GLAPIENTRY', + ], + }], + ['OS=="linux"', { + 'link_settings': { + 'libraries': [ + '-ldl', + '-lm', + '-lstdc++', + ], + }, + }], + ], + 'include_dirs': [ + 'src/src/mapi', + 'src/src/mesa', + 'src/src/mesa/drivers', + '<(generated_src_dir)/mesa', + ], + 'msvs_disabled_warnings': [ + 4005, 4018, 4065, 4090, 4099, 4291, 4345, 4267, + ], + 'sources': [ + 'src/src/mesa/drivers/common/driverfuncs.c', + 'src/src/mesa/drivers/common/driverfuncs.h', + 'src/src/mesa/drivers/common/meta.c', + 'src/src/mesa/drivers/common/meta.h', + 'src/src/mesa/drivers/osmesa/osmesa.c', + 'src/src/mesa/drivers/osmesa/osmesa.def', + ], + 'variables': { + 'clang_warning_flags_unset': [ + # Don't warn about string->bool used in asserts. + '-Wstring-conversion', + ], + }, + }, + ], + 'conditions': [ + ['OS=="android"', { + 'targets': [ + { + # Copies libosmesa.so to the out/$BUILDTYPE/lib/ directory so that + # the write_ordered_libraries.py script won't assume it to be a + # system library. This will cause the library to be stripped allowing + # targets to embed it in the to-be-generated APK. + 'target_name': 'osmesa_in_lib_dir', + 'type': 'none', + 'dependencies': [ + 'osmesa', + ], + 'actions': [ + { + 'action_name': 'copy_libosmesa', + 'inputs': ['<(PRODUCT_DIR)/libosmesa.so'], + 'outputs': ['<(SHARED_LIB_DIR)/libosmesa.so'], + 'action': [ + 'cp', + '<(PRODUCT_DIR)/libosmesa.so', + '<(SHARED_LIB_DIR)/libosmesa.so', + ], + }, + ], + }, + ], + }], + ], +} diff --git a/engine/src/flutter/third_party/mesa/redirectoutput.py b/engine/src/flutter/third_party/mesa/redirectoutput.py new file mode 100644 index 0000000000..bcd235552d --- /dev/null +++ b/engine/src/flutter/third_party/mesa/redirectoutput.py @@ -0,0 +1,29 @@ +# 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. + +import os +import os.path +import subprocess +import sys + +if len(sys.argv) < 3: + print "Usage: %s OUTPUTFILE SCRIPTNAME ARGUMENTS" % sys.argv[0] + print "Re-execs the python interpreter against SCRIPTNAME with ARGS," + print "redirecting output to OUTPUTFILE." + sys.exit(1) + +abs_outputfile = os.path.abspath(sys.argv[1]) +abs_outputdir = os.path.dirname(abs_outputfile) + +if not os.path.isdir(abs_outputdir): + os.makedirs(abs_outputdir) + +ret = 0 + +with open(abs_outputfile, "w") as f: + ret = subprocess.Popen([sys.executable] + sys.argv[2:], stdout=f).wait() + +if ret: + os.remove(abs_outputfile) + sys.exit(ret) diff --git a/engine/src/flutter/third_party/mockito/BUILD.gn b/engine/src/flutter/third_party/mockito/BUILD.gn new file mode 100644 index 0000000000..2f03b7c789 --- /dev/null +++ b/engine/src/flutter/third_party/mockito/BUILD.gn @@ -0,0 +1,28 @@ +# Copyright 2014 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. + +import("//build/config/android/rules.gni") + +# GYP: //third_party/mockito.gyp:cglib_and_asm_jar +java_prebuilt("cglib_and_asm_java") { + jar_path = "src/lib/repackaged/cglib-and-asm-1.0.jar" +} + +# GYP: //third_party/mockito.gyp:objenesis_jar +java_prebuilt("objenesis_java") { + jar_path = "src/lib/run/objenesis-2.1.jar" +} + +# GYP: //third_party/mockito.gyp:mockito_jar +java_library("mockito_java") { + chromium_code = false + testonly = true + deps = [ + ":cglib_and_asm_java", + ":objenesis_java", + "../junit:junit", + "../junit:hamcrest", + ] + DEPRECATED_java_in_dir = "src/src" +} diff --git a/engine/src/flutter/third_party/mockito/LICENSE b/engine/src/flutter/third_party/mockito/LICENSE new file mode 100644 index 0000000000..5a311f7c5c --- /dev/null +++ b/engine/src/flutter/third_party/mockito/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2007 Mockito contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/engine/src/flutter/third_party/mockito/OWNERS b/engine/src/flutter/third_party/mockito/OWNERS new file mode 100644 index 0000000000..bd675fb8c4 --- /dev/null +++ b/engine/src/flutter/third_party/mockito/OWNERS @@ -0,0 +1,2 @@ +jbudorick@chromium.org +klundberg@chromium.org diff --git a/engine/src/flutter/third_party/mockito/README.chromium b/engine/src/flutter/third_party/mockito/README.chromium new file mode 100644 index 0000000000..1d932add92 --- /dev/null +++ b/engine/src/flutter/third_party/mockito/README.chromium @@ -0,0 +1,9 @@ +Name: Mockito +URL: https://code.google.com/p/mockito +Version: 1.10.5 +License: MIT +License File: NOT_SHIPPED +Security Critical: no +License Android Compatible: yes +Description: Mockito is a java mocking framework. +Local Modifications: None diff --git a/engine/src/flutter/third_party/mockito/mockito.gyp b/engine/src/flutter/third_party/mockito/mockito.gyp new file mode 100644 index 0000000000..d9a9db2032 --- /dev/null +++ b/engine/src/flutter/third_party/mockito/mockito.gyp @@ -0,0 +1,47 @@ +# Copyright 2014 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. + +{ + 'targets': [ + { + # GN: //third_party/mockito:cglib_and_asm_java + 'target_name': 'cglib_and_asm_jar', + 'type': 'none', + 'variables': { + 'jar_path': 'src/lib/repackaged/cglib-and-asm-1.0.jar', + }, + 'includes': [ + '../../build/host_prebuilt_jar.gypi', + ] + }, + { + # GN: //third_party/mockito:objenesis_java + 'target_name': 'objenesis_jar', + 'type': 'none', + 'variables': { + 'jar_path': 'src/lib/run/objenesis-2.1.jar', + }, + 'includes': [ + '../../build/host_prebuilt_jar.gypi', + ] + }, + { + # GN: //third_party/mockito:mockito_java + 'target_name': 'mockito_jar', + 'type': 'none', + 'dependencies': [ + 'cglib_and_asm_jar', + 'objenesis_jar', + '../junit/junit.gyp:junit_jar', + ], + 'variables': { + 'src_paths': [ 'src/src' ], + }, + 'includes': [ + '../../build/host_jar.gypi', + ], + }, + ], +} + diff --git a/engine/src/flutter/third_party/modp_b64/BUILD.gn b/engine/src/flutter/third_party/modp_b64/BUILD.gn new file mode 100644 index 0000000000..2ec992b4c9 --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/BUILD.gn @@ -0,0 +1,11 @@ +# 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. + +source_set("modp_b64") { + sources = [ + "modp_b64.cc", + "modp_b64.h", + "modp_b64_data.h", + ] +} diff --git a/engine/src/flutter/third_party/modp_b64/DEPS b/engine/src/flutter/third_party/modp_b64/DEPS new file mode 100644 index 0000000000..8061b5a3f3 --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + '+build', +] \ No newline at end of file diff --git a/engine/src/flutter/third_party/modp_b64/LICENSE b/engine/src/flutter/third_party/modp_b64/LICENSE new file mode 100644 index 0000000000..55af76f3eb --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/LICENSE @@ -0,0 +1,33 @@ + * MODP_B64 - High performance base64 encoder/decoder + * Version 1.3 -- 17-Mar-2006 + * http://modp.com/release/base64 + * + * Copyright (c) 2005, 2006 Nick Galbreath -- nickg [at] modp [dot] com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of the modp.com nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/engine/src/flutter/third_party/modp_b64/OWNERS b/engine/src/flutter/third_party/modp_b64/OWNERS new file mode 100644 index 0000000000..0144bacf05 --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/OWNERS @@ -0,0 +1,2 @@ +jschuh@chromium.org +rsleevi@chromium.org diff --git a/engine/src/flutter/third_party/modp_b64/README.chromium b/engine/src/flutter/third_party/modp_b64/README.chromium new file mode 100644 index 0000000000..35b9e54840 --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/README.chromium @@ -0,0 +1,15 @@ +Name: modp base64 decoder +Short Name: stringencoders +URL: http://code.google.com/p/stringencoders/ +Version: unknown +License: BSD +Security Critical: yes + +Description: +The modp_b64.c file was modified to remove the inclusion of modp's config.h +and to fix compilation errors that occur under VC8. The file was renamed +modp_b64.cc to force it to be compiled as C++ so that the inclusion of +basictypes.h could be possible. + +The modp_b64.cc and modp_b64.h files were modified to make them safe on +64-bit systems. diff --git a/engine/src/flutter/third_party/modp_b64/modp_b64.cc b/engine/src/flutter/third_party/modp_b64/modp_b64.cc new file mode 100644 index 0000000000..e5f6cf1024 --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/modp_b64.cc @@ -0,0 +1,265 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4: */ +/** + * \file + *
+ * MODP_B64 - High performance base64 encoder/decoder
+ * Version 1.3 -- 17-Mar-2006
+ * http://modp.com/release/base64
+ *
+ * Copyright © 2005, 2006  Nick Galbreath -- nickg [at] modp [dot] com
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ *   Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ *   Neither the name of the modp.com nor the names of its
+ *   contributors may be used to endorse or promote products derived from
+ *   this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This is the standard "new" BSD license:
+ * http://www.opensource.org/licenses/bsd-license.php
+ * 
+ */ + +/* public header */ +#include "modp_b64.h" + +/* + * If you are ripping this out of the library, comment out the next + * line and uncomment the next lines as approrpiate + */ +//#include "config.h" + +/* if on motoral, sun, ibm; uncomment this */ +/* #define WORDS_BIGENDIAN 1 */ +/* else for Intel, Amd; uncomment this */ +/* #undef WORDS_BIGENDIAN */ + +#include "modp_b64_data.h" + +#define BADCHAR 0x01FFFFFF + +/** + * you can control if we use padding by commenting out this + * next line. However, I highly recommend you use padding and not + * using it should only be for compatability with a 3rd party. + * Also, 'no padding' is not tested! + */ +#define DOPAD 1 + +/* + * if we aren't doing padding + * set the pad character to NULL + */ +#ifndef DOPAD +#undef CHARPAD +#define CHARPAD '\0' +#endif + +size_t modp_b64_encode(char* dest, const char* str, size_t len) +{ + size_t i = 0; + uint8_t* p = (uint8_t*) dest; + + /* unsigned here is important! */ + uint8_t t1, t2, t3; + + if (len > 2) { + for (; i < len - 2; i += 3) { + t1 = str[i]; t2 = str[i+1]; t3 = str[i+2]; + *p++ = e0[t1]; + *p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *p++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *p++ = e2[t3]; + } + } + + switch (len - i) { + case 0: + break; + case 1: + t1 = str[i]; + *p++ = e0[t1]; + *p++ = e1[(t1 & 0x03) << 4]; + *p++ = CHARPAD; + *p++ = CHARPAD; + break; + default: /* case 2 */ + t1 = str[i]; t2 = str[i+1]; + *p++ = e0[t1]; + *p++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *p++ = e2[(t2 & 0x0F) << 2]; + *p++ = CHARPAD; + } + + *p = '\0'; + return p - (uint8_t*)dest; +} + +#ifdef WORDS_BIGENDIAN /* BIG ENDIAN -- SUN / IBM / MOTOROLA */ +int modp_b64_decode(char* dest, const char* src, int len) +{ + if (len == 0) return 0; + +#ifdef DOPAD + /* if padding is used, then the message must be at least + 4 chars and be a multiple of 4. + there can be at most 2 pad chars at the end */ + if (len < 4 || (len % 4 != 0)) return MODP_B64_ERROR; + if (src[len-1] == CHARPAD) { + len--; + if (src[len -1] == CHARPAD) { + len--; + } + } +#endif /* DOPAD */ + + size_t i; + int leftover = len % 4; + size_t chunks = (leftover == 0) ? len / 4 - 1 : len /4; + + uint8_t* p = (uint8_t*) dest; + uint32_t x = 0; + uint32_t* destInt = (uint32_t*) p; + uint32_t* srcInt = (uint32_t*) src; + uint32_t y = *srcInt++; + for (i = 0; i < chunks; ++i) { + x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] | + d2[y >> 8 & 0xff] | d3[y & 0xff]; + + if (x >= BADCHAR) return MODP_B64_ERROR; + *destInt = x << 8; + p += 3; + destInt = (uint32_t*)p; + y = *srcInt++; + } + + switch (leftover) { + case 0: + x = d0[y >> 24 & 0xff] | d1[y >> 16 & 0xff] | + d2[y >> 8 & 0xff] | d3[y & 0xff]; + if (x >= BADCHAR) return MODP_B64_ERROR; + *p++ = ((uint8_t*)&x)[1]; + *p++ = ((uint8_t*)&x)[2]; + *p = ((uint8_t*)&x)[3]; + return (chunks+1)*3; + case 1: + x = d3[y >> 24]; + *p = (uint8_t)x; + break; + case 2: + x = d3[y >> 24] *64 + d3[(y >> 16) & 0xff]; + *p = (uint8_t)(x >> 4); + break; + default: /* case 3 */ + x = (d3[y >> 24] *64 + d3[(y >> 16) & 0xff])*64 + + d3[(y >> 8) & 0xff]; + *p++ = (uint8_t) (x >> 10); + *p = (uint8_t) (x >> 2); + break; + } + + if (x >= BADCHAR) return MODP_B64_ERROR; + return 3*chunks + (6*leftover)/8; +} + +#else /* LITTLE ENDIAN -- INTEL AND FRIENDS */ + +size_t modp_b64_decode(char* dest, const char* src, size_t len) +{ + if (len == 0) return 0; + +#ifdef DOPAD + /* + * if padding is used, then the message must be at least + * 4 chars and be a multiple of 4 + */ + if (len < 4 || (len % 4 != 0)) return MODP_B64_ERROR; /* error */ + /* there can be at most 2 pad chars at the end */ + if (src[len-1] == CHARPAD) { + len--; + if (src[len -1] == CHARPAD) { + len--; + } + } +#endif + + size_t i; + int leftover = len % 4; + size_t chunks = (leftover == 0) ? len / 4 - 1 : len /4; + + uint8_t* p = (uint8_t*)dest; + uint32_t x = 0; + uint32_t* destInt = (uint32_t*) p; + uint32_t* srcInt = (uint32_t*) src; + uint32_t y = *srcInt++; + for (i = 0; i < chunks; ++i) { + x = d0[y & 0xff] | + d1[(y >> 8) & 0xff] | + d2[(y >> 16) & 0xff] | + d3[(y >> 24) & 0xff]; + + if (x >= BADCHAR) return MODP_B64_ERROR; + *destInt = x ; + p += 3; + destInt = (uint32_t*)p; + y = *srcInt++;} + + + switch (leftover) { + case 0: + x = d0[y & 0xff] | + d1[(y >> 8) & 0xff] | + d2[(y >> 16) & 0xff] | + d3[(y >> 24) & 0xff]; + + if (x >= BADCHAR) return MODP_B64_ERROR; + *p++ = ((uint8_t*)(&x))[0]; + *p++ = ((uint8_t*)(&x))[1]; + *p = ((uint8_t*)(&x))[2]; + return (chunks+1)*3; + break; + case 1: /* with padding this is an impossible case */ + x = d0[y & 0xff]; + *p = *((uint8_t*)(&x)); // i.e. first char/byte in int + break; + case 2: // * case 2, 1 output byte */ + x = d0[y & 0xff] | d1[y >> 8 & 0xff]; + *p = *((uint8_t*)(&x)); // i.e. first char + break; + default: /* case 3, 2 output bytes */ + x = d0[y & 0xff] | + d1[y >> 8 & 0xff ] | + d2[y >> 16 & 0xff]; /* 0x3c */ + *p++ = ((uint8_t*)(&x))[0]; + *p = ((uint8_t*)(&x))[1]; + break; + } + + if (x >= BADCHAR) return MODP_B64_ERROR; + + return 3*chunks + (6*leftover)/8; +} + +#endif /* if bigendian / else / endif */ diff --git a/engine/src/flutter/third_party/modp_b64/modp_b64.gyp b/engine/src/flutter/third_party/modp_b64/modp_b64.gyp new file mode 100644 index 0000000000..6176c5deea --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/modp_b64.gyp @@ -0,0 +1,48 @@ +# Copyright (c) 2009 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. + +{ + 'targets': [ + { + 'target_name': 'modp_b64', + 'type': 'static_library', + 'toolsets': ['host', 'target'], + 'sources': [ + 'modp_b64.cc', + 'modp_b64.h', + 'modp_b64_data.h', + ], + 'include_dirs': [ + '../..', + ], + }, + ], + 'conditions': [ + ['OS == "win" and target_arch=="ia32"', { + # Even if we are building the browser for Win32, we need a few modules + # to be built for Win64, and this is a prerequsite. + 'targets': [ + { + 'target_name': 'modp_b64_win64', + 'type': 'static_library', + # We can't use dynamic_annotations target for win64 build since it is + # a 32-bit library. + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'modp_b64.cc', + 'modp_b64.h', + 'modp_b64_data.h', + ], + 'configurations': { + 'Common_Base': { + 'msvs_target_platform': 'x64', + }, + }, + }, + ], + }], + ], +} diff --git a/engine/src/flutter/third_party/modp_b64/modp_b64.h b/engine/src/flutter/third_party/modp_b64/modp_b64.h new file mode 100644 index 0000000000..3270e5fdf1 --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/modp_b64.h @@ -0,0 +1,171 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4: */ + +/** + * \file + *
+ * High performance base64 encoder / decoder
+ * Version 1.3 -- 17-Mar-2006
+ *
+ * Copyright © 2005, 2006, Nick Galbreath -- nickg [at] modp [dot] com
+ * All rights reserved.
+ *
+ * http://modp.com/release/base64
+ *
+ * Released under bsd license.  See modp_b64.c for details.
+ * 
+ * + * The default implementation is the standard b64 encoding with padding. + * It's easy to change this to use "URL safe" characters and to remove + * padding. See the modp_b64.c source code for details. + * + */ + +#ifndef MODP_B64 +#define MODP_B64 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Encode a raw binary string into base 64. + * src contains the bytes + * len contains the number of bytes in the src + * dest should be allocated by the caller to contain + * at least modp_b64_encode_len(len) bytes (see below) + * This will contain the null-terminated b64 encoded result + * returns length of the destination string plus the ending null byte + * i.e. the result will be equal to strlen(dest) + 1 + * + * Example + * + * \code + * char* src = ...; + * int srclen = ...; //the length of number of bytes in src + * char* dest = (char*) malloc(modp_b64_encode_len); + * int len = modp_b64_encode(dest, src, sourcelen); + * if (len == -1) { + * printf("Error\n"); + * } else { + * printf("b64 = %s\n", dest); + * } + * \endcode + * + */ +size_t modp_b64_encode(char* dest, const char* str, size_t len); + +/** + * Decode a base64 encoded string + * + * src should contain exactly len bytes of b64 characters. + * if src contains -any- non-base characters (such as white + * space, -1 is returned. + * + * dest should be allocated by the caller to contain at least + * len * 3 / 4 bytes. + * + * Returns the length (strlen) of the output, or -1 if unable to + * decode + * + * \code + * char* src = ...; + * int srclen = ...; // or if you don't know use strlen(src) + * char* dest = (char*) malloc(modp_b64_decode_len(srclen)); + * int len = modp_b64_decode(dest, src, sourcelen); + * if (len == -1) { error } + * \endcode + */ +size_t modp_b64_decode(char* dest, const char* src, size_t len); + +/** + * Given a source string of length len, this returns the amount of + * memory the destination string should have. + * + * remember, this is integer math + * 3 bytes turn into 4 chars + * ceiling[len / 3] * 4 + 1 + * + * +1 is for any extra null. + */ +#define modp_b64_encode_len(A) ((A+2)/3 * 4 + 1) + +/** + * Given a base64 string of length len, + * this returns the amount of memory required for output string + * It maybe be more than the actual number of bytes written. + * NOTE: remember this is integer math + * this allocates a bit more memory than traditional versions of b64 + * decode 4 chars turn into 3 bytes + * floor[len * 3/4] + 2 + */ +#define modp_b64_decode_len(A) (A / 4 * 3 + 2) + +/** + * Will return the strlen of the output from encoding. + * This may be less than the required number of bytes allocated. + * + * This allows you to 'deserialized' a struct + * \code + * char* b64encoded = "..."; + * int len = strlen(b64encoded); + * + * struct datastuff foo; + * if (modp_b64_encode_strlen(sizeof(struct datastuff)) != len) { + * // wrong size + * return false; + * } else { + * // safe to do; + * if (modp_b64_decode((char*) &foo, b64encoded, len) == -1) { + * // bad characters + * return false; + * } + * } + * // foo is filled out now + * \endcode + */ +#define modp_b64_encode_strlen(A) ((A + 2)/ 3 * 4) + +#define MODP_B64_ERROR ((size_t)-1) + +#ifdef __cplusplus +} + +#include + +inline std::string& modp_b64_encode(std::string& s) +{ + std::string x(modp_b64_encode_len(s.size()), '\0'); + size_t d = modp_b64_encode(const_cast(x.data()), s.data(), (int)s.size()); + x.erase(d, std::string::npos); + s.swap(x); + return s; +} + +/** + * base 64 decode a string (self-modifing) + * On failure, the string is empty. + * + * This function is for C++ only (duh) + * + * \param[in,out] s the string to be decoded + * \return a reference to the input string + */ +inline std::string& modp_b64_decode(std::string& s) +{ + std::string x(modp_b64_decode_len(s.size()), '\0'); + size_t d = modp_b64_decode(const_cast(x.data()), s.data(), (int)s.size()); + if (d == MODP_B64_ERROR) { + x.clear(); + } else { + x.erase(d, std::string::npos); + } + s.swap(x); + return s; +} + +#endif /* __cplusplus */ + +#endif /* MODP_B64 */ diff --git a/engine/src/flutter/third_party/modp_b64/modp_b64_data.h b/engine/src/flutter/third_party/modp_b64/modp_b64_data.h new file mode 100644 index 0000000000..fb85890d9d --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/modp_b64_data.h @@ -0,0 +1,482 @@ +#include "build/build_config.h" +#include + +#define CHAR62 '+' +#define CHAR63 '/' +#define CHARPAD '=' +static const char e0[256] = { + 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', + 'C', 'C', 'D', 'D', 'D', 'D', 'E', 'E', 'E', 'E', + 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', + 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', + 'K', 'K', 'K', 'K', 'L', 'L', 'L', 'L', 'M', 'M', + 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', + 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', + 'R', 'R', 'S', 'S', 'S', 'S', 'T', 'T', 'T', 'T', + 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', + 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', + 'Z', 'Z', 'Z', 'Z', 'a', 'a', 'a', 'a', 'b', 'b', + 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', + 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', + 'g', 'g', 'h', 'h', 'h', 'h', 'i', 'i', 'i', 'i', + 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', + 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', + 'o', 'o', 'o', 'o', 'p', 'p', 'p', 'p', 'q', 'q', + 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', + 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', + 'v', 'v', 'w', 'w', 'w', 'w', 'x', 'x', 'x', 'x', + 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', + '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', + '3', '3', '3', '3', '4', '4', '4', '4', '5', '5', + '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', + '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', + '+', '+', '/', '/', '/', '/' +}; + +static const char e1[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '+', '/' +}; + +static const char e2[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '+', '/' +}; + + + +#ifdef WORDS_BIGENDIAN + + +/* SPECIAL DECODE TABLES FOR BIG ENDIAN (IBM/MOTOROLA/SUN) CPUS */ + +static const uint32_t d0[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00f80000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00fc0000, +0x00d00000, 0x00d40000, 0x00d80000, 0x00dc0000, 0x00e00000, 0x00e40000, +0x00e80000, 0x00ec0000, 0x00f00000, 0x00f40000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00040000, 0x00080000, 0x000c0000, 0x00100000, 0x00140000, 0x00180000, +0x001c0000, 0x00200000, 0x00240000, 0x00280000, 0x002c0000, 0x00300000, +0x00340000, 0x00380000, 0x003c0000, 0x00400000, 0x00440000, 0x00480000, +0x004c0000, 0x00500000, 0x00540000, 0x00580000, 0x005c0000, 0x00600000, +0x00640000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00680000, 0x006c0000, 0x00700000, 0x00740000, 0x00780000, +0x007c0000, 0x00800000, 0x00840000, 0x00880000, 0x008c0000, 0x00900000, +0x00940000, 0x00980000, 0x009c0000, 0x00a00000, 0x00a40000, 0x00a80000, +0x00ac0000, 0x00b00000, 0x00b40000, 0x00b80000, 0x00bc0000, 0x00c00000, +0x00c40000, 0x00c80000, 0x00cc0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d1[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0003e000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0003f000, +0x00034000, 0x00035000, 0x00036000, 0x00037000, 0x00038000, 0x00039000, +0x0003a000, 0x0003b000, 0x0003c000, 0x0003d000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, +0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, +0x0000d000, 0x0000e000, 0x0000f000, 0x00010000, 0x00011000, 0x00012000, +0x00013000, 0x00014000, 0x00015000, 0x00016000, 0x00017000, 0x00018000, +0x00019000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0001a000, 0x0001b000, 0x0001c000, 0x0001d000, 0x0001e000, +0x0001f000, 0x00020000, 0x00021000, 0x00022000, 0x00023000, 0x00024000, +0x00025000, 0x00026000, 0x00027000, 0x00028000, 0x00029000, 0x0002a000, +0x0002b000, 0x0002c000, 0x0002d000, 0x0002e000, 0x0002f000, 0x00030000, +0x00031000, 0x00032000, 0x00033000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d2[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00000f80, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000fc0, +0x00000d00, 0x00000d40, 0x00000d80, 0x00000dc0, 0x00000e00, 0x00000e40, +0x00000e80, 0x00000ec0, 0x00000f00, 0x00000f40, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000040, 0x00000080, 0x000000c0, 0x00000100, 0x00000140, 0x00000180, +0x000001c0, 0x00000200, 0x00000240, 0x00000280, 0x000002c0, 0x00000300, +0x00000340, 0x00000380, 0x000003c0, 0x00000400, 0x00000440, 0x00000480, +0x000004c0, 0x00000500, 0x00000540, 0x00000580, 0x000005c0, 0x00000600, +0x00000640, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00000680, 0x000006c0, 0x00000700, 0x00000740, 0x00000780, +0x000007c0, 0x00000800, 0x00000840, 0x00000880, 0x000008c0, 0x00000900, +0x00000940, 0x00000980, 0x000009c0, 0x00000a00, 0x00000a40, 0x00000a80, +0x00000ac0, 0x00000b00, 0x00000b40, 0x00000b80, 0x00000bc0, 0x00000c00, +0x00000c40, 0x00000c80, 0x00000cc0, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d3[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000003e, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000003f, +0x00000034, 0x00000035, 0x00000036, 0x00000037, 0x00000038, 0x00000039, +0x0000003a, 0x0000003b, 0x0000003c, 0x0000003d, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000001, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00000006, +0x00000007, 0x00000008, 0x00000009, 0x0000000a, 0x0000000b, 0x0000000c, +0x0000000d, 0x0000000e, 0x0000000f, 0x00000010, 0x00000011, 0x00000012, +0x00000013, 0x00000014, 0x00000015, 0x00000016, 0x00000017, 0x00000018, +0x00000019, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000001a, 0x0000001b, 0x0000001c, 0x0000001d, 0x0000001e, +0x0000001f, 0x00000020, 0x00000021, 0x00000022, 0x00000023, 0x00000024, +0x00000025, 0x00000026, 0x00000027, 0x00000028, 0x00000029, 0x0000002a, +0x0000002b, 0x0000002c, 0x0000002d, 0x0000002e, 0x0000002f, 0x00000030, +0x00000031, 0x00000032, 0x00000033, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +#else + + +/* SPECIAL DECODE TABLES FOR LITTLE ENDIAN (INTEL) CPUS */ + +static const uint32_t d0[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, +0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, +0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, +0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, +0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, +0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, +0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, +0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, +0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, +0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, +0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d1[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, +0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, +0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, +0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, +0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, +0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, +0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, +0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, +0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, +0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, +0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d2[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, +0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, +0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, +0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, +0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, +0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, +0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, +0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, +0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, +0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, +0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +static const uint32_t d3[256] = { +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, +0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, +0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, +0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, +0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, +0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, +0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, +0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, +0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, +0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, +0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, +0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, +0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff +}; + + +#endif diff --git a/engine/src/flutter/third_party/modp_b64/modp_b64_nacl.gyp b/engine/src/flutter/third_party/modp_b64/modp_b64_nacl.gyp new file mode 100644 index 0000000000..e2f4a25424 --- /dev/null +++ b/engine/src/flutter/third_party/modp_b64/modp_b64_nacl.gyp @@ -0,0 +1,26 @@ +# Copyright 2014 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. + +{ + 'includes': [ + '../../native_client/build/untrusted.gypi', + ], + 'targets': [ + { + 'target_name': 'modp_b64_nacl', + 'type': 'none', + 'variables': { + 'nlib_target': 'libmodp_b64_nacl.a', + 'build_glibc': 0, + 'build_newlib': 1, + 'build_pnacl_newlib': 1, + }, + 'sources': [ + 'modp_b64.cc', + 'modp_b64.h', + 'modp_b64_data.h', + ], + }, + ], +} diff --git a/engine/src/flutter/third_party/ply/LICENSE b/engine/src/flutter/third_party/ply/LICENSE new file mode 100644 index 0000000000..b273866345 --- /dev/null +++ b/engine/src/flutter/third_party/ply/LICENSE @@ -0,0 +1,30 @@ +PLY (Python Lex-Yacc) Version 3.4 + +Copyright (C) 2001-2011, +David M. Beazley (Dabeaz LLC) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the David Beazley or Dabeaz LLC may be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/engine/src/flutter/third_party/ply/README b/engine/src/flutter/third_party/ply/README new file mode 100644 index 0000000000..f384d1a938 --- /dev/null +++ b/engine/src/flutter/third_party/ply/README @@ -0,0 +1,271 @@ +PLY (Python Lex-Yacc) Version 3.4 + +Copyright (C) 2001-2011, +David M. Beazley (Dabeaz LLC) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the David Beazley or Dabeaz LLC may be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Introduction +============ + +PLY is a 100% Python implementation of the common parsing tools lex +and yacc. Here are a few highlights: + + - PLY is very closely modeled after traditional lex/yacc. + If you know how to use these tools in C, you will find PLY + to be similar. + + - PLY provides *very* extensive error reporting and diagnostic + information to assist in parser construction. The original + implementation was developed for instructional purposes. As + a result, the system tries to identify the most common types + of errors made by novice users. + + - PLY provides full support for empty productions, error recovery, + precedence specifiers, and moderately ambiguous grammars. + + - Parsing is based on LR-parsing which is fast, memory efficient, + better suited to large grammars, and which has a number of nice + properties when dealing with syntax errors and other parsing problems. + Currently, PLY builds its parsing tables using the LALR(1) + algorithm used in yacc. + + - PLY uses Python introspection features to build lexers and parsers. + This greatly simplifies the task of parser construction since it reduces + the number of files and eliminates the need to run a separate lex/yacc + tool before running your program. + + - PLY can be used to build parsers for "real" programming languages. + Although it is not ultra-fast due to its Python implementation, + PLY can be used to parse grammars consisting of several hundred + rules (as might be found for a language like C). The lexer and LR + parser are also reasonably efficient when parsing typically + sized programs. People have used PLY to build parsers for + C, C++, ADA, and other real programming languages. + +How to Use +========== + +PLY consists of two files : lex.py and yacc.py. These are contained +within the 'ply' directory which may also be used as a Python package. +To use PLY, simply copy the 'ply' directory to your project and import +lex and yacc from the associated 'ply' package. For example: + + import ply.lex as lex + import ply.yacc as yacc + +Alternatively, you can copy just the files lex.py and yacc.py +individually and use them as modules. For example: + + import lex + import yacc + +The file setup.py can be used to install ply using distutils. + +The file doc/ply.html contains complete documentation on how to use +the system. + +The example directory contains several different examples including a +PLY specification for ANSI C as given in K&R 2nd Ed. + +A simple example is found at the end of this document + +Requirements +============ +PLY requires the use of Python 2.2 or greater. However, you should +use the latest Python release if possible. It should work on just +about any platform. PLY has been tested with both CPython and Jython. +It also seems to work with IronPython. + +Resources +========= +More information about PLY can be obtained on the PLY webpage at: + + http://www.dabeaz.com/ply + +For a detailed overview of parsing theory, consult the excellent +book "Compilers : Principles, Techniques, and Tools" by Aho, Sethi, and +Ullman. The topics found in "Lex & Yacc" by Levine, Mason, and Brown +may also be useful. + +A Google group for PLY can be found at + + http://groups.google.com/group/ply-hack + +Acknowledgments +=============== +A special thanks is in order for all of the students in CS326 who +suffered through about 25 different versions of these tools :-). + +The CHANGES file acknowledges those who have contributed patches. + +Elias Ioup did the first implementation of LALR(1) parsing in PLY-1.x. +Andrew Waters and Markus Schoepflin were instrumental in reporting bugs +and testing a revised LALR(1) implementation for PLY-2.0. + +Special Note for PLY-3.0 +======================== +PLY-3.0 the first PLY release to support Python 3. However, backwards +compatibility with Python 2.2 is still preserved. PLY provides dual +Python 2/3 compatibility by restricting its implementation to a common +subset of basic language features. You should not convert PLY using +2to3--it is not necessary and may in fact break the implementation. + +Example +======= + +Here is a simple example showing a PLY implementation of a calculator +with variables. + +# ----------------------------------------------------------------------------- +# calc.py +# +# A simple calculator with variables. +# ----------------------------------------------------------------------------- + +tokens = ( + 'NAME','NUMBER', + 'PLUS','MINUS','TIMES','DIVIDE','EQUALS', + 'LPAREN','RPAREN', + ) + +# Tokens + +t_PLUS = r'\+' +t_MINUS = r'-' +t_TIMES = r'\*' +t_DIVIDE = r'/' +t_EQUALS = r'=' +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' + +def t_NUMBER(t): + r'\d+' + t.value = int(t.value) + return t + +# Ignored characters +t_ignore = " \t" + +def t_newline(t): + r'\n+' + t.lexer.lineno += t.value.count("\n") + +def t_error(t): + print("Illegal character '%s'" % t.value[0]) + t.lexer.skip(1) + +# Build the lexer +import ply.lex as lex +lex.lex() + +# Precedence rules for the arithmetic operators +precedence = ( + ('left','PLUS','MINUS'), + ('left','TIMES','DIVIDE'), + ('right','UMINUS'), + ) + +# dictionary of names (for storing variables) +names = { } + +def p_statement_assign(p): + 'statement : NAME EQUALS expression' + names[p[1]] = p[3] + +def p_statement_expr(p): + 'statement : expression' + print(p[1]) + +def p_expression_binop(p): + '''expression : expression PLUS expression + | expression MINUS expression + | expression TIMES expression + | expression DIVIDE expression''' + if p[2] == '+' : p[0] = p[1] + p[3] + elif p[2] == '-': p[0] = p[1] - p[3] + elif p[2] == '*': p[0] = p[1] * p[3] + elif p[2] == '/': p[0] = p[1] / p[3] + +def p_expression_uminus(p): + 'expression : MINUS expression %prec UMINUS' + p[0] = -p[2] + +def p_expression_group(p): + 'expression : LPAREN expression RPAREN' + p[0] = p[2] + +def p_expression_number(p): + 'expression : NUMBER' + p[0] = p[1] + +def p_expression_name(p): + 'expression : NAME' + try: + p[0] = names[p[1]] + except LookupError: + print("Undefined name '%s'" % p[1]) + p[0] = 0 + +def p_error(p): + print("Syntax error at '%s'" % p.value) + +import ply.yacc as yacc +yacc.yacc() + +while 1: + try: + s = raw_input('calc > ') # use input() on Python 3 + except EOFError: + break + yacc.parse(s) + + +Bug Reports and Patches +======================= +My goal with PLY is to simply have a decent lex/yacc implementation +for Python. As a general rule, I don't spend huge amounts of time +working on it unless I receive very specific bug reports and/or +patches to fix problems. I also try to incorporate submitted feature +requests and enhancements into each new version. To contact me about +bugs and/or new features, please send email to dave@dabeaz.com. + +In addition there is a Google group for discussing PLY related issues at + + http://groups.google.com/group/ply-hack + +-- Dave + + + + + + + + + diff --git a/engine/src/flutter/third_party/ply/README.chromium b/engine/src/flutter/third_party/ply/README.chromium new file mode 100644 index 0000000000..6466e5428a --- /dev/null +++ b/engine/src/flutter/third_party/ply/README.chromium @@ -0,0 +1,21 @@ +Name: PLY (Python Lex-Yacc) +Current version: 3.4 +URL: http://www.dabeaz.com/ply/ply-3.4.tar.gz +License: BSD +License File: LICENSE +Security Critical: no +Version: 3.4 + +This directory contains a copy of these ply-3.4 components: + +README ply-3.4/README +Sources ply-3.4/ply/__init__.py + ply-3.4/ply/lex.py + ply-3.4/ply/yacc.py + + +The license is in LICENSE. + +Modifications made with initial commit: +- Added the file README.chromium (this file) +- Applies license.patch diff --git a/engine/src/flutter/third_party/ply/__init__.py b/engine/src/flutter/third_party/ply/__init__.py new file mode 100644 index 0000000000..f3da03eade --- /dev/null +++ b/engine/src/flutter/third_party/ply/__init__.py @@ -0,0 +1,36 @@ +# PLY package +# Author: David Beazley (dave@dabeaz.com) +# ----------------------------------------------------------------------------- +# ply: yacc.py +# +# Copyright (C) 2001-2011, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +__all__ = ['lex','yacc'] diff --git a/engine/src/flutter/third_party/ply/lex.py b/engine/src/flutter/third_party/ply/lex.py new file mode 100644 index 0000000000..bd32da9327 --- /dev/null +++ b/engine/src/flutter/third_party/ply/lex.py @@ -0,0 +1,1058 @@ +# ----------------------------------------------------------------------------- +# ply: lex.py +# +# Copyright (C) 2001-2011, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +__version__ = "3.4" +__tabversion__ = "3.2" # Version of table file used + +import re, sys, types, copy, os + +# This tuple contains known string types +try: + # Python 2.6 + StringTypes = (types.StringType, types.UnicodeType) +except AttributeError: + # Python 3.0 + StringTypes = (str, bytes) + +# Extract the code attribute of a function. Different implementations +# are for Python 2/3 compatibility. + +if sys.version_info[0] < 3: + def func_code(f): + return f.func_code +else: + def func_code(f): + return f.__code__ + +# This regular expression is used to match valid token names +_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') + +# Exception thrown when invalid token encountered and no default error +# handler is defined. + +class LexError(Exception): + def __init__(self,message,s): + self.args = (message,) + self.text = s + +# Token class. This class is used to represent the tokens produced. +class LexToken(object): + def __str__(self): + return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos) + def __repr__(self): + return str(self) + +# This object is a stand-in for a logging object created by the +# logging module. + +class PlyLogger(object): + def __init__(self,f): + self.f = f + def critical(self,msg,*args,**kwargs): + self.f.write((msg % args) + "\n") + + def warning(self,msg,*args,**kwargs): + self.f.write("WARNING: "+ (msg % args) + "\n") + + def error(self,msg,*args,**kwargs): + self.f.write("ERROR: " + (msg % args) + "\n") + + info = critical + debug = critical + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self,name): + return self + def __call__(self,*args,**kwargs): + return self + +# ----------------------------------------------------------------------------- +# === Lexing Engine === +# +# The following Lexer class implements the lexer runtime. There are only +# a few public methods and attributes: +# +# input() - Store a new string in the lexer +# token() - Get the next token +# clone() - Clone the lexer +# +# lineno - Current line number +# lexpos - Current position in the input string +# ----------------------------------------------------------------------------- + +class Lexer: + def __init__(self): + self.lexre = None # Master regular expression. This is a list of + # tuples (re,findex) where re is a compiled + # regular expression and findex is a list + # mapping regex group numbers to rules + self.lexretext = None # Current regular expression strings + self.lexstatere = {} # Dictionary mapping lexer states to master regexs + self.lexstateretext = {} # Dictionary mapping lexer states to regex strings + self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names + self.lexstate = "INITIAL" # Current lexer state + self.lexstatestack = [] # Stack of lexer states + self.lexstateinfo = None # State information + self.lexstateignore = {} # Dictionary of ignored characters for each state + self.lexstateerrorf = {} # Dictionary of error functions for each state + self.lexreflags = 0 # Optional re compile flags + self.lexdata = None # Actual input data (as a string) + self.lexpos = 0 # Current position in input text + self.lexlen = 0 # Length of the input text + self.lexerrorf = None # Error rule (if any) + self.lextokens = None # List of valid tokens + self.lexignore = "" # Ignored characters + self.lexliterals = "" # Literal characters that can be passed through + self.lexmodule = None # Module + self.lineno = 1 # Current line number + self.lexoptimize = 0 # Optimized mode + + def clone(self,object=None): + c = copy.copy(self) + + # If the object parameter has been supplied, it means we are attaching the + # lexer to a new object. In this case, we have to rebind all methods in + # the lexstatere and lexstateerrorf tables. + + if object: + newtab = { } + for key, ritem in self.lexstatere.items(): + newre = [] + for cre, findex in ritem: + newfindex = [] + for f in findex: + if not f or not f[0]: + newfindex.append(f) + continue + newfindex.append((getattr(object,f[0].__name__),f[1])) + newre.append((cre,newfindex)) + newtab[key] = newre + c.lexstatere = newtab + c.lexstateerrorf = { } + for key, ef in self.lexstateerrorf.items(): + c.lexstateerrorf[key] = getattr(object,ef.__name__) + c.lexmodule = object + return c + + # ------------------------------------------------------------ + # writetab() - Write lexer information to a table file + # ------------------------------------------------------------ + def writetab(self,tabfile,outputdir=""): + if isinstance(tabfile,types.ModuleType): + return + basetabfilename = tabfile.split(".")[-1] + filename = os.path.join(outputdir,basetabfilename)+".py" + tf = open(filename,"w") + tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__)) + tf.write("_tabversion = %s\n" % repr(__version__)) + tf.write("_lextokens = %s\n" % repr(self.lextokens)) + tf.write("_lexreflags = %s\n" % repr(self.lexreflags)) + tf.write("_lexliterals = %s\n" % repr(self.lexliterals)) + tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo)) + + tabre = { } + # Collect all functions in the initial state + initial = self.lexstatere["INITIAL"] + initialfuncs = [] + for part in initial: + for f in part[1]: + if f and f[0]: + initialfuncs.append(f) + + for key, lre in self.lexstatere.items(): + titem = [] + for i in range(len(lre)): + titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i]))) + tabre[key] = titem + + tf.write("_lexstatere = %s\n" % repr(tabre)) + tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore)) + + taberr = { } + for key, ef in self.lexstateerrorf.items(): + if ef: + taberr[key] = ef.__name__ + else: + taberr[key] = None + tf.write("_lexstateerrorf = %s\n" % repr(taberr)) + tf.close() + + # ------------------------------------------------------------ + # readtab() - Read lexer information from a tab file + # ------------------------------------------------------------ + def readtab(self,tabfile,fdict): + if isinstance(tabfile,types.ModuleType): + lextab = tabfile + else: + if sys.version_info[0] < 3: + exec("import %s as lextab" % tabfile) + else: + env = { } + exec("import %s as lextab" % tabfile, env,env) + lextab = env['lextab'] + + if getattr(lextab,"_tabversion","0.0") != __version__: + raise ImportError("Inconsistent PLY version") + + self.lextokens = lextab._lextokens + self.lexreflags = lextab._lexreflags + self.lexliterals = lextab._lexliterals + self.lexstateinfo = lextab._lexstateinfo + self.lexstateignore = lextab._lexstateignore + self.lexstatere = { } + self.lexstateretext = { } + for key,lre in lextab._lexstatere.items(): + titem = [] + txtitem = [] + for i in range(len(lre)): + titem.append((re.compile(lre[i][0],lextab._lexreflags | re.VERBOSE),_names_to_funcs(lre[i][1],fdict))) + txtitem.append(lre[i][0]) + self.lexstatere[key] = titem + self.lexstateretext[key] = txtitem + self.lexstateerrorf = { } + for key,ef in lextab._lexstateerrorf.items(): + self.lexstateerrorf[key] = fdict[ef] + self.begin('INITIAL') + + # ------------------------------------------------------------ + # input() - Push a new string into the lexer + # ------------------------------------------------------------ + def input(self,s): + # Pull off the first character to see if s looks like a string + c = s[:1] + if not isinstance(c,StringTypes): + raise ValueError("Expected a string") + self.lexdata = s + self.lexpos = 0 + self.lexlen = len(s) + + # ------------------------------------------------------------ + # begin() - Changes the lexing state + # ------------------------------------------------------------ + def begin(self,state): + if not state in self.lexstatere: + raise ValueError("Undefined state") + self.lexre = self.lexstatere[state] + self.lexretext = self.lexstateretext[state] + self.lexignore = self.lexstateignore.get(state,"") + self.lexerrorf = self.lexstateerrorf.get(state,None) + self.lexstate = state + + # ------------------------------------------------------------ + # push_state() - Changes the lexing state and saves old on stack + # ------------------------------------------------------------ + def push_state(self,state): + self.lexstatestack.append(self.lexstate) + self.begin(state) + + # ------------------------------------------------------------ + # pop_state() - Restores the previous state + # ------------------------------------------------------------ + def pop_state(self): + self.begin(self.lexstatestack.pop()) + + # ------------------------------------------------------------ + # current_state() - Returns the current lexing state + # ------------------------------------------------------------ + def current_state(self): + return self.lexstate + + # ------------------------------------------------------------ + # skip() - Skip ahead n characters + # ------------------------------------------------------------ + def skip(self,n): + self.lexpos += n + + # ------------------------------------------------------------ + # opttoken() - Return the next token from the Lexer + # + # Note: This function has been carefully implemented to be as fast + # as possible. Don't make changes unless you really know what + # you are doing + # ------------------------------------------------------------ + def token(self): + # Make local copies of frequently referenced attributes + lexpos = self.lexpos + lexlen = self.lexlen + lexignore = self.lexignore + lexdata = self.lexdata + + while lexpos < lexlen: + # This code provides some short-circuit code for whitespace, tabs, and other ignored characters + if lexdata[lexpos] in lexignore: + lexpos += 1 + continue + + # Look for a regular expression match + for lexre,lexindexfunc in self.lexre: + m = lexre.match(lexdata,lexpos) + if not m: continue + + # Create a token for return + tok = LexToken() + tok.value = m.group() + tok.lineno = self.lineno + tok.lexpos = lexpos + + i = m.lastindex + func,tok.type = lexindexfunc[i] + + if not func: + # If no token type was set, it's an ignored token + if tok.type: + self.lexpos = m.end() + return tok + else: + lexpos = m.end() + break + + lexpos = m.end() + + # If token is processed by a function, call it + + tok.lexer = self # Set additional attributes useful in token rules + self.lexmatch = m + self.lexpos = lexpos + + newtok = func(tok) + + # Every function must return a token, if nothing, we just move to next token + if not newtok: + lexpos = self.lexpos # This is here in case user has updated lexpos. + lexignore = self.lexignore # This is here in case there was a state change + break + + # Verify type of the token. If not in the token map, raise an error + if not self.lexoptimize: + if not newtok.type in self.lextokens: + raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( + func_code(func).co_filename, func_code(func).co_firstlineno, + func.__name__, newtok.type),lexdata[lexpos:]) + + return newtok + else: + # No match, see if in literals + if lexdata[lexpos] in self.lexliterals: + tok = LexToken() + tok.value = lexdata[lexpos] + tok.lineno = self.lineno + tok.type = tok.value + tok.lexpos = lexpos + self.lexpos = lexpos + 1 + return tok + + # No match. Call t_error() if defined. + if self.lexerrorf: + tok = LexToken() + tok.value = self.lexdata[lexpos:] + tok.lineno = self.lineno + tok.type = "error" + tok.lexer = self + tok.lexpos = lexpos + self.lexpos = lexpos + newtok = self.lexerrorf(tok) + if lexpos == self.lexpos: + # Error method didn't change text position at all. This is an error. + raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) + lexpos = self.lexpos + if not newtok: continue + return newtok + + self.lexpos = lexpos + raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:]) + + self.lexpos = lexpos + 1 + if self.lexdata is None: + raise RuntimeError("No input string given with input()") + return None + + # Iterator interface + def __iter__(self): + return self + + def next(self): + t = self.token() + if t is None: + raise StopIteration + return t + + __next__ = next + +# ----------------------------------------------------------------------------- +# ==== Lex Builder === +# +# The functions and classes below are used to collect lexing information +# and build a Lexer object from it. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + try: + raise RuntimeError + except RuntimeError: + e,b,t = sys.exc_info() + f = t.tb_frame + while levels > 0: + f = f.f_back + levels -= 1 + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + + return ldict + +# ----------------------------------------------------------------------------- +# _funcs_to_names() +# +# Given a list of regular expression functions, this converts it to a list +# suitable for output to a table file +# ----------------------------------------------------------------------------- + +def _funcs_to_names(funclist,namelist): + result = [] + for f,name in zip(funclist,namelist): + if f and f[0]: + result.append((name, f[1])) + else: + result.append(f) + return result + +# ----------------------------------------------------------------------------- +# _names_to_funcs() +# +# Given a list of regular expression function names, this converts it back to +# functions. +# ----------------------------------------------------------------------------- + +def _names_to_funcs(namelist,fdict): + result = [] + for n in namelist: + if n and n[0]: + result.append((fdict[n[0]],n[1])) + else: + result.append(n) + return result + +# ----------------------------------------------------------------------------- +# _form_master_re() +# +# This function takes a list of all of the regex components and attempts to +# form the master regular expression. Given limitations in the Python re +# module, it may be necessary to break the master regex into separate expressions. +# ----------------------------------------------------------------------------- + +def _form_master_re(relist,reflags,ldict,toknames): + if not relist: return [] + regex = "|".join(relist) + try: + lexre = re.compile(regex,re.VERBOSE | reflags) + + # Build the index to function map for the matching engine + lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1) + lexindexnames = lexindexfunc[:] + + for f,i in lexre.groupindex.items(): + handle = ldict.get(f,None) + if type(handle) in (types.FunctionType, types.MethodType): + lexindexfunc[i] = (handle,toknames[f]) + lexindexnames[i] = f + elif handle is not None: + lexindexnames[i] = f + if f.find("ignore_") > 0: + lexindexfunc[i] = (None,None) + else: + lexindexfunc[i] = (None, toknames[f]) + + return [(lexre,lexindexfunc)],[regex],[lexindexnames] + except Exception: + m = int(len(relist)/2) + if m == 0: m = 1 + llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames) + rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames) + return llist+rlist, lre+rre, lnames+rnames + +# ----------------------------------------------------------------------------- +# def _statetoken(s,names) +# +# Given a declaration name s of the form "t_" and a dictionary whose keys are +# state names, this function returns a tuple (states,tokenname) where states +# is a tuple of state names and tokenname is the name of the token. For example, +# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') +# ----------------------------------------------------------------------------- + +def _statetoken(s,names): + nonstate = 1 + parts = s.split("_") + for i in range(1,len(parts)): + if not parts[i] in names and parts[i] != 'ANY': break + if i > 1: + states = tuple(parts[1:i]) + else: + states = ('INITIAL',) + + if 'ANY' in states: + states = tuple(names) + + tokenname = "_".join(parts[i:]) + return (states,tokenname) + + +# ----------------------------------------------------------------------------- +# LexerReflect() +# +# This class represents information needed to build a lexer as extracted from a +# user's input file. +# ----------------------------------------------------------------------------- +class LexerReflect(object): + def __init__(self,ldict,log=None,reflags=0): + self.ldict = ldict + self.error_func = None + self.tokens = [] + self.reflags = reflags + self.stateinfo = { 'INITIAL' : 'inclusive'} + self.files = {} + self.error = 0 + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_tokens() + self.get_literals() + self.get_states() + self.get_rules() + + # Validate all of the information + def validate_all(self): + self.validate_tokens() + self.validate_literals() + self.validate_rules() + return self.error + + # Get the tokens map + def get_tokens(self): + tokens = self.ldict.get("tokens",None) + if not tokens: + self.log.error("No token list is defined") + self.error = 1 + return + + if not isinstance(tokens,(list, tuple)): + self.log.error("tokens must be a list or tuple") + self.error = 1 + return + + if not tokens: + self.log.error("tokens is empty") + self.error = 1 + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + terminals = {} + for n in self.tokens: + if not _is_identifier.match(n): + self.log.error("Bad token name '%s'",n) + self.error = 1 + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the literals specifier + def get_literals(self): + self.literals = self.ldict.get("literals","") + + # Validate literals + def validate_literals(self): + try: + for c in self.literals: + if not isinstance(c,StringTypes) or len(c) > 1: + self.log.error("Invalid literal %s. Must be a single character", repr(c)) + self.error = 1 + continue + + except TypeError: + self.log.error("Invalid literals specification. literals must be a sequence of characters") + self.error = 1 + + def get_states(self): + self.states = self.ldict.get("states",None) + # Build statemap + if self.states: + if not isinstance(self.states,(tuple,list)): + self.log.error("states must be defined as a tuple or list") + self.error = 1 + else: + for s in self.states: + if not isinstance(s,tuple) or len(s) != 2: + self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s)) + self.error = 1 + continue + name, statetype = s + if not isinstance(name,StringTypes): + self.log.error("State name %s must be a string", repr(name)) + self.error = 1 + continue + if not (statetype == 'inclusive' or statetype == 'exclusive'): + self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name) + self.error = 1 + continue + if name in self.stateinfo: + self.log.error("State '%s' already defined",name) + self.error = 1 + continue + self.stateinfo[name] = statetype + + # Get all of the symbols with a t_ prefix and sort them into various + # categories (functions, strings, error functions, and ignore characters) + + def get_rules(self): + tsymbols = [f for f in self.ldict if f[:2] == 't_' ] + + # Now build up a list of functions and a list of strings + + self.toknames = { } # Mapping of symbols to token names + self.funcsym = { } # Symbols defined as functions + self.strsym = { } # Symbols defined as strings + self.ignore = { } # Ignore strings by state + self.errorf = { } # Error functions by state + + for s in self.stateinfo: + self.funcsym[s] = [] + self.strsym[s] = [] + + if len(tsymbols) == 0: + self.log.error("No rules of the form t_rulename are defined") + self.error = 1 + return + + for f in tsymbols: + t = self.ldict[f] + states, tokname = _statetoken(f,self.stateinfo) + self.toknames[f] = tokname + + if hasattr(t,"__call__"): + if tokname == 'error': + for s in states: + self.errorf[s] = t + elif tokname == 'ignore': + line = func_code(t).co_firstlineno + file = func_code(t).co_filename + self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__) + self.error = 1 + else: + for s in states: + self.funcsym[s].append((f,t)) + elif isinstance(t, StringTypes): + if tokname == 'ignore': + for s in states: + self.ignore[s] = t + if "\\" in t: + self.log.warning("%s contains a literal backslash '\\'",f) + + elif tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", f) + self.error = 1 + else: + for s in states: + self.strsym[s].append((f,t)) + else: + self.log.error("%s not defined as a function or string", f) + self.error = 1 + + # Sort the functions by line number + for f in self.funcsym.values(): + if sys.version_info[0] < 3: + f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno)) + else: + # Python 3.0 + f.sort(key=lambda x: func_code(x[1]).co_firstlineno) + + # Sort the strings by regular expression length + for s in self.strsym.values(): + if sys.version_info[0] < 3: + s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1]))) + else: + # Python 3.0 + s.sort(key=lambda x: len(x[1]),reverse=True) + + # Validate all of the t_rules collected + def validate_rules(self): + for state in self.stateinfo: + # Validate all rules defined by functions + + + + for fname, f in self.funcsym[state]: + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + self.files[file] = 1 + + tokname = self.toknames[fname] + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = func_code(f).co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) + self.error = 1 + continue + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) + self.error = 1 + continue + + if not f.__doc__: + self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__) + self.error = 1 + continue + + try: + c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | self.reflags) + if c.match(""): + self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__) + self.error = 1 + except re.error: + _etype, e, _etrace = sys.exc_info() + self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e) + if '#' in f.__doc__: + self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__) + self.error = 1 + + # Validate all rules defined by strings + for name,r in self.strsym[state]: + tokname = self.toknames[name] + if tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", name) + self.error = 1 + continue + + if not tokname in self.tokens and tokname.find("ignore_") < 0: + self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname) + self.error = 1 + continue + + try: + c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags) + if (c.match("")): + self.log.error("Regular expression for rule '%s' matches empty string",name) + self.error = 1 + except re.error: + _etype, e, _etrace = sys.exc_info() + self.log.error("Invalid regular expression for rule '%s'. %s",name,e) + if '#' in r: + self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name) + self.error = 1 + + if not self.funcsym[state] and not self.strsym[state]: + self.log.error("No rules defined for state '%s'",state) + self.error = 1 + + # Validate the error function + efunc = self.errorf.get(state,None) + if efunc: + f = efunc + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + self.files[file] = 1 + + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = func_code(f).co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) + self.error = 1 + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) + self.error = 1 + + for f in self.files: + self.validate_file(f) + + + # ----------------------------------------------------------------------------- + # validate_file() + # + # This checks to see if there are duplicated t_rulename() functions or strings + # in the parser input file. This is done using a simple regular expression + # match on each line in the given file. + # ----------------------------------------------------------------------------- + + def validate_file(self,filename): + import os.path + base,ext = os.path.splitext(filename) + if ext != '.py': return # No idea what the file is. Return OK + + try: + f = open(filename) + lines = f.readlines() + f.close() + except IOError: + return # Couldn't find the file. Don't worry about it + + fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') + sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') + + counthash = { } + linen = 1 + for l in lines: + m = fre.match(l) + if not m: + m = sre.match(l) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev) + self.error = 1 + linen += 1 + +# ----------------------------------------------------------------------------- +# lex(module) +# +# Build all of the regular expression rules from definitions in the supplied module +# ----------------------------------------------------------------------------- +def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None): + global lexer + ldict = None + stateinfo = { 'INITIAL' : 'inclusive'} + lexobj = Lexer() + lexobj.lexoptimize = optimize + global token,input + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + if debug: + if debuglog is None: + debuglog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the lexer + if object: module = object + + if module: + _items = [(k,getattr(module,k)) for k in dir(module)] + ldict = dict(_items) + else: + ldict = get_caller_module_dict(2) + + # Collect parser information from the dictionary + linfo = LexerReflect(ldict,log=errorlog,reflags=reflags) + linfo.get_all() + if not optimize: + if linfo.validate_all(): + raise SyntaxError("Can't build lexer") + + if optimize and lextab: + try: + lexobj.readtab(lextab,ldict) + token = lexobj.token + input = lexobj.input + lexer = lexobj + return lexobj + + except ImportError: + pass + + # Dump some basic debugging information + if debug: + debuglog.info("lex: tokens = %r", linfo.tokens) + debuglog.info("lex: literals = %r", linfo.literals) + debuglog.info("lex: states = %r", linfo.stateinfo) + + # Build a dictionary of valid token names + lexobj.lextokens = { } + for n in linfo.tokens: + lexobj.lextokens[n] = 1 + + # Get literals specification + if isinstance(linfo.literals,(list,tuple)): + lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) + else: + lexobj.lexliterals = linfo.literals + + # Get the stateinfo dictionary + stateinfo = linfo.stateinfo + + regexs = { } + # Build the master regular expressions + for state in stateinfo: + regex_list = [] + + # Add rules defined by functions first + for fname, f in linfo.funcsym[state]: + line = func_code(f).co_firstlineno + file = func_code(f).co_filename + regex_list.append("(?P<%s>%s)" % (fname,f.__doc__)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state) + + # Now add all of the simple rules + for name,r in linfo.strsym[state]: + regex_list.append("(?P<%s>%s)" % (name,r)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state) + + regexs[state] = regex_list + + # Build the master regular expressions + + if debug: + debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====") + + for state in regexs: + lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames) + lexobj.lexstatere[state] = lexre + lexobj.lexstateretext[state] = re_text + lexobj.lexstaterenames[state] = re_names + if debug: + for i in range(len(re_text)): + debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i]) + + # For inclusive states, we need to add the regular expressions from the INITIAL state + for state,stype in stateinfo.items(): + if state != "INITIAL" and stype == 'inclusive': + lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) + lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) + lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) + + lexobj.lexstateinfo = stateinfo + lexobj.lexre = lexobj.lexstatere["INITIAL"] + lexobj.lexretext = lexobj.lexstateretext["INITIAL"] + lexobj.lexreflags = reflags + + # Set up ignore variables + lexobj.lexstateignore = linfo.ignore + lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","") + + # Set up error functions + lexobj.lexstateerrorf = linfo.errorf + lexobj.lexerrorf = linfo.errorf.get("INITIAL",None) + if not lexobj.lexerrorf: + errorlog.warning("No t_error rule is defined") + + # Check state information for ignore and error rules + for s,stype in stateinfo.items(): + if stype == 'exclusive': + if not s in linfo.errorf: + errorlog.warning("No error rule is defined for exclusive state '%s'", s) + if not s in linfo.ignore and lexobj.lexignore: + errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) + elif stype == 'inclusive': + if not s in linfo.errorf: + linfo.errorf[s] = linfo.errorf.get("INITIAL",None) + if not s in linfo.ignore: + linfo.ignore[s] = linfo.ignore.get("INITIAL","") + + # Create global versions of the token() and input() functions + token = lexobj.token + input = lexobj.input + lexer = lexobj + + # If in optimize mode, we write the lextab + if lextab and optimize: + lexobj.writetab(lextab,outputdir) + + return lexobj + +# ----------------------------------------------------------------------------- +# runmain() +# +# This runs the lexer as a main program +# ----------------------------------------------------------------------------- + +def runmain(lexer=None,data=None): + if not data: + try: + filename = sys.argv[1] + f = open(filename) + data = f.read() + f.close() + except IndexError: + sys.stdout.write("Reading from standard input (type EOF to end):\n") + data = sys.stdin.read() + + if lexer: + _input = lexer.input + else: + _input = input + _input(data) + if lexer: + _token = lexer.token + else: + _token = token + + while 1: + tok = _token() + if not tok: break + sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos)) + +# ----------------------------------------------------------------------------- +# @TOKEN(regex) +# +# This decorator function can be used to set the regex expression on a function +# when its docstring might need to be set in an alternative way +# ----------------------------------------------------------------------------- + +def TOKEN(r): + def set_doc(f): + if hasattr(r,"__call__"): + f.__doc__ = r.__doc__ + else: + f.__doc__ = r + return f + return set_doc + +# Alternative spelling of the TOKEN decorator +Token = TOKEN + diff --git a/engine/src/flutter/third_party/ply/license.patch b/engine/src/flutter/third_party/ply/license.patch new file mode 100644 index 0000000000..7b2621fc46 --- /dev/null +++ b/engine/src/flutter/third_party/ply/license.patch @@ -0,0 +1,41 @@ +diff --git a/third_party/ply/__init__.py b/third_party/ply/__init__.py +index 853a985..f3da03e 100644 +--- a/third_party/ply/__init__.py ++++ b/third_party/ply/__init__.py +@@ -1,4 +1,36 @@ + # PLY package + # Author: David Beazley (dave@dabeaz.com) ++# ----------------------------------------------------------------------------- ++# ply: yacc.py ++# ++# Copyright (C) 2001-2011, ++# David M. Beazley (Dabeaz LLC) ++# All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions are ++# met: ++# ++# * Redistributions of source code must retain the above copyright notice, ++# this list of conditions and the following disclaimer. ++# * Redistributions in binary form must reproduce the above copyright notice, ++# this list of conditions and the following disclaimer in the documentation ++# and/or other materials provided with the distribution. ++# * Neither the name of the David Beazley or Dabeaz LLC may be used to ++# endorse or promote products derived from this software without ++# specific prior written permission. ++# ++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++# ----------------------------------------------------------------------------- + + __all__ = ['lex','yacc'] diff --git a/engine/src/flutter/third_party/ply/yacc.py b/engine/src/flutter/third_party/ply/yacc.py new file mode 100644 index 0000000000..f70439ea5e --- /dev/null +++ b/engine/src/flutter/third_party/ply/yacc.py @@ -0,0 +1,3276 @@ +# ----------------------------------------------------------------------------- +# ply: yacc.py +# +# Copyright (C) 2001-2011, +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# +# This implements an LR parser that is constructed from grammar rules defined +# as Python functions. The grammer is specified by supplying the BNF inside +# Python documentation strings. The inspiration for this technique was borrowed +# from John Aycock's Spark parsing system. PLY might be viewed as cross between +# Spark and the GNU bison utility. +# +# The current implementation is only somewhat object-oriented. The +# LR parser itself is defined in terms of an object (which allows multiple +# parsers to co-exist). However, most of the variables used during table +# construction are defined in terms of global variables. Users shouldn't +# notice unless they are trying to define multiple parsers at the same +# time using threads (in which case they should have their head examined). +# +# This implementation supports both SLR and LALR(1) parsing. LALR(1) +# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), +# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, +# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced +# by the more efficient DeRemer and Pennello algorithm. +# +# :::::::: WARNING ::::::: +# +# Construction of LR parsing tables is fairly complicated and expensive. +# To make this module run fast, a *LOT* of work has been put into +# optimization---often at the expensive of readability and what might +# consider to be good Python "coding style." Modify the code at your +# own risk! +# ---------------------------------------------------------------------------- + +__version__ = "3.4" +__tabversion__ = "3.2" # Table version + +#----------------------------------------------------------------------------- +# === User configurable parameters === +# +# Change these to modify the default behavior of yacc (if you wish) +#----------------------------------------------------------------------------- + +yaccdebug = 1 # Debugging mode. If set, yacc generates a + # a 'parser.out' file in the current directory + +debug_file = 'parser.out' # Default name of the debugging file +tab_module = 'parsetab' # Default name of the table module +default_lr = 'LALR' # Default LR table generation method + +error_count = 3 # Number of symbols that must be shifted to leave recovery mode + +yaccdevel = 0 # Set to True if developing yacc. This turns off optimized + # implementations of certain functions. + +resultlimit = 40 # Size limit of results when running in debug mode. + +pickle_protocol = 0 # Protocol to use when writing pickle files + +import re, types, sys, os.path + +# Compatibility function for python 2.6/3.0 +if sys.version_info[0] < 3: + def func_code(f): + return f.func_code +else: + def func_code(f): + return f.__code__ + +# Compatibility +try: + MAXINT = sys.maxint +except AttributeError: + MAXINT = sys.maxsize + +# Python 2.x/3.0 compatibility. +def load_ply_lex(): + if sys.version_info[0] < 3: + import lex + else: + import ply.lex as lex + return lex + +# This object is a stand-in for a logging object created by the +# logging module. PLY will use this by default to create things +# such as the parser.out file. If a user wants more detailed +# information, they can create their own logging object and pass +# it into PLY. + +class PlyLogger(object): + def __init__(self,f): + self.f = f + def debug(self,msg,*args,**kwargs): + self.f.write((msg % args) + "\n") + info = debug + + def warning(self,msg,*args,**kwargs): + self.f.write("WARNING: "+ (msg % args) + "\n") + + def error(self,msg,*args,**kwargs): + self.f.write("ERROR: " + (msg % args) + "\n") + + critical = debug + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self,name): + return self + def __call__(self,*args,**kwargs): + return self + +# Exception raised for yacc-related errors +class YaccError(Exception): pass + +# Format the result message that the parser produces when running in debug mode. +def format_result(r): + repr_str = repr(r) + if '\n' in repr_str: repr_str = repr(repr_str) + if len(repr_str) > resultlimit: + repr_str = repr_str[:resultlimit]+" ..." + result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str) + return result + + +# Format stack entries when the parser is running in debug mode +def format_stack_entry(r): + repr_str = repr(r) + if '\n' in repr_str: repr_str = repr(repr_str) + if len(repr_str) < 16: + return repr_str + else: + return "<%s @ 0x%x>" % (type(r).__name__,id(r)) + +#----------------------------------------------------------------------------- +# === LR Parsing Engine === +# +# The following classes are used for the LR parser itself. These are not +# used during table construction and are independent of the actual LR +# table generation algorithm +#----------------------------------------------------------------------------- + +# This class is used to hold non-terminal grammar symbols during parsing. +# It normally has the following attributes set: +# .type = Grammar symbol type +# .value = Symbol value +# .lineno = Starting line number +# .endlineno = Ending line number (optional, set automatically) +# .lexpos = Starting lex position +# .endlexpos = Ending lex position (optional, set automatically) + +class YaccSymbol: + def __str__(self): return self.type + def __repr__(self): return str(self) + +# This class is a wrapper around the objects actually passed to each +# grammar rule. Index lookup and assignment actually assign the +# .value attribute of the underlying YaccSymbol object. +# The lineno() method returns the line number of a given +# item (or 0 if not defined). The linespan() method returns +# a tuple of (startline,endline) representing the range of lines +# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) +# representing the range of positional information for a symbol. + +class YaccProduction: + def __init__(self,s,stack=None): + self.slice = s + self.stack = stack + self.lexer = None + self.parser= None + def __getitem__(self,n): + if n >= 0: return self.slice[n].value + else: return self.stack[n].value + + def __setitem__(self,n,v): + self.slice[n].value = v + + def __getslice__(self,i,j): + return [s.value for s in self.slice[i:j]] + + def __len__(self): + return len(self.slice) + + def lineno(self,n): + return getattr(self.slice[n],"lineno",0) + + def set_lineno(self,n,lineno): + self.slice[n].lineno = lineno + + def linespan(self,n): + startline = getattr(self.slice[n],"lineno",0) + endline = getattr(self.slice[n],"endlineno",startline) + return startline,endline + + def lexpos(self,n): + return getattr(self.slice[n],"lexpos",0) + + def lexspan(self,n): + startpos = getattr(self.slice[n],"lexpos",0) + endpos = getattr(self.slice[n],"endlexpos",startpos) + return startpos,endpos + + def error(self): + raise SyntaxError + + +# ----------------------------------------------------------------------------- +# == LRParser == +# +# The LR Parsing engine. +# ----------------------------------------------------------------------------- + +class LRParser: + def __init__(self,lrtab,errorf): + self.productions = lrtab.lr_productions + self.action = lrtab.lr_action + self.goto = lrtab.lr_goto + self.errorfunc = errorf + + def errok(self): + self.errorok = 1 + + def restart(self): + del self.statestack[:] + del self.symstack[:] + sym = YaccSymbol() + sym.type = '$end' + self.symstack.append(sym) + self.statestack.append(0) + + def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + if debug or yaccdevel: + if isinstance(debug,int): + debug = PlyLogger(sys.stderr) + return self.parsedebug(input,lexer,debug,tracking,tokenfunc) + elif tracking: + return self.parseopt(input,lexer,debug,tracking,tokenfunc) + else: + return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc) + + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parsedebug(). + # + # This is the debugging enabled version of parse(). All changes made to the + # parsing engine should be made here. For the non-debugging version, + # copy this code to a method parseopt() and delete all of the sections + # enclosed in: + # + # #--! DEBUG + # statements + # #--! DEBUG + # + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # --! DEBUG + debug.info("PLY: PARSE DEBUG START") + # --! DEBUG + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = "$end" + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + # --! DEBUG + debug.debug('') + debug.debug('State : %s', state) + # --! DEBUG + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = "$end" + + # --! DEBUG + debug.debug('Stack : %s', + ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + # --! DEBUG + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + # --! DEBUG + debug.debug("Action : Shift and goto state %s", t) + # --! DEBUG + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + # --! DEBUG + if plen: + debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, "["+",".join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+"]",-t) + else: + debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t) + + # --! DEBUG + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # --! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1,"endlineno",t1.lineno) + sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) + + # --! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + # --! DEBUG + debug.info("Result : %s", format_result(pslice[0])) + # --! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + # --! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + # --! TRACKING + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + # --! DEBUG + debug.info("Result : %s", format_result(pslice[0])) + # --! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n,"value",None) + # --! DEBUG + debug.info("Done : Returning %s", format_result(result)) + debug.info("PLY: PARSE DEBUG END") + # --! DEBUG + return result + + if t == None: + + # --! DEBUG + debug.error('Error : %s', + ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + # --! DEBUG + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == "$end": + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != "$end": + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == "$end": + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt(). + # + # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY. + # Edit the debug version above, then copy any modifications to the method + # below while removing #--! DEBUG sections. + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + + def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # --! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1,"endlineno",t1.lineno) + sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) + + # --! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + # --! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + # --! TRACKING + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + return getattr(n,"value",None) + + if t == None: + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt_notrack(). + # + # Optimized version of parseopt() with line number tracking removed. + # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove + # code in the #--! TRACKING sections + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): + lookahead = None # Current lookahead symbol + lookaheadstack = [ ] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + # If no lexer was given, we will try to use the lex module + if not lexer: + lex = load_ply_lex() + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set up the state and symbol stacks + + statestack = [ ] # Stack of parsing states + self.statestack = statestack + symstack = [ ] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while 1: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: errorcount -=1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + del statestack[-plen:] + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + targ = [ sym ] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) + symstack.pop() + statestack.pop() + state = statestack[-1] + sym.type = 'error' + lookahead = sym + errorcount = error_count + self.errorok = 0 + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + return getattr(n,"value",None) + + if t == None: + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = 0 + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + global errok,token,restart + errok = self.errok # Set some special functions available in error recovery + token = get_token + restart = self.restart + if errtoken and not hasattr(errtoken,'lexer'): + errtoken.lexer = lexer + tok = self.errorfunc(errtoken) + del errok, token, restart # Delete special functions + + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken,"lineno"): lineno = lookahead.lineno + else: lineno = 0 + if lineno: + sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) + else: + sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) + else: + sys.stderr.write("yacc: Parse error in input. EOF\n") + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + t = YaccSymbol() + t.type = 'error' + if hasattr(lookahead,"lineno"): + t.lineno = lookahead.lineno + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + symstack.pop() + statestack.pop() + state = statestack[-1] # Potential bug fix + + continue + + # Call an error function here + raise RuntimeError("yacc: internal parser error!!!\n") + +# ----------------------------------------------------------------------------- +# === Grammar Representation === +# +# The following functions, classes, and variables are used to represent and +# manipulate the rules that make up a grammar. +# ----------------------------------------------------------------------------- + +import re + +# regex matching identifiers +_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') + +# ----------------------------------------------------------------------------- +# class Production: +# +# This class stores the raw information about a single production or grammar rule. +# A grammar rule refers to a specification such as this: +# +# expr : expr PLUS term +# +# Here are the basic attributes defined on all productions +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','PLUS','term'] +# prec - Production precedence level +# number - Production number. +# func - Function that executes on reduce +# file - File where production function is defined +# lineno - Line number where production function is defined +# +# The following attributes are defined or optional. +# +# len - Length of the production (number of symbols on right hand side) +# usyms - Set of unique symbols found in the production +# ----------------------------------------------------------------------------- + +class Production(object): + reduced = 0 + def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0): + self.name = name + self.prod = tuple(prod) + self.number = number + self.func = func + self.callable = None + self.file = file + self.line = line + self.prec = precedence + + # Internal settings used during table construction + + self.len = len(self.prod) # Length of the production + + # Create a list of unique production symbols used in the production + self.usyms = [ ] + for s in self.prod: + if s not in self.usyms: + self.usyms.append(s) + + # List of all LR items for the production + self.lr_items = [] + self.lr_next = None + + # Create a string representation + if self.prod: + self.str = "%s -> %s" % (self.name," ".join(self.prod)) + else: + self.str = "%s -> " % self.name + + def __str__(self): + return self.str + + def __repr__(self): + return "Production("+str(self)+")" + + def __len__(self): + return len(self.prod) + + def __nonzero__(self): + return 1 + + def __getitem__(self,index): + return self.prod[index] + + # Return the nth lr_item from the production (or None if at the end) + def lr_item(self,n): + if n > len(self.prod): return None + p = LRItem(self,n) + + # Precompute the list of productions immediately following. Hack. Remove later + try: + p.lr_after = Prodnames[p.prod[n+1]] + except (IndexError,KeyError): + p.lr_after = [] + try: + p.lr_before = p.prod[n-1] + except IndexError: + p.lr_before = None + + return p + + # Bind the production function name to a callable + def bind(self,pdict): + if self.func: + self.callable = pdict[self.func] + +# This class serves as a minimal standin for Production objects when +# reading table data from files. It only contains information +# actually used by the LR parsing engine, plus some additional +# debugging information. +class MiniProduction(object): + def __init__(self,str,name,len,func,file,line): + self.name = name + self.len = len + self.func = func + self.callable = None + self.file = file + self.line = line + self.str = str + def __str__(self): + return self.str + def __repr__(self): + return "MiniProduction(%s)" % self.str + + # Bind the production function name to a callable + def bind(self,pdict): + if self.func: + self.callable = pdict[self.func] + + +# ----------------------------------------------------------------------------- +# class LRItem +# +# This class represents a specific stage of parsing a production rule. For +# example: +# +# expr : expr . PLUS term +# +# In the above, the "." represents the current location of the parse. Here +# basic attributes: +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] +# number - Production number. +# +# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' +# then lr_next refers to 'expr -> expr PLUS . term' +# lr_index - LR item index (location of the ".") in the prod list. +# lookaheads - LALR lookahead symbols for this item +# len - Length of the production (number of symbols on right hand side) +# lr_after - List of all productions that immediately follow +# lr_before - Grammar symbol immediately before +# ----------------------------------------------------------------------------- + +class LRItem(object): + def __init__(self,p,n): + self.name = p.name + self.prod = list(p.prod) + self.number = p.number + self.lr_index = n + self.lookaheads = { } + self.prod.insert(n,".") + self.prod = tuple(self.prod) + self.len = len(self.prod) + self.usyms = p.usyms + + def __str__(self): + if self.prod: + s = "%s -> %s" % (self.name," ".join(self.prod)) + else: + s = "%s -> " % self.name + return s + + def __repr__(self): + return "LRItem("+str(self)+")" + +# ----------------------------------------------------------------------------- +# rightmost_terminal() +# +# Return the rightmost terminal from a list of symbols. Used in add_production() +# ----------------------------------------------------------------------------- +def rightmost_terminal(symbols, terminals): + i = len(symbols) - 1 + while i >= 0: + if symbols[i] in terminals: + return symbols[i] + i -= 1 + return None + +# ----------------------------------------------------------------------------- +# === GRAMMAR CLASS === +# +# The following class represents the contents of the specified grammar along +# with various computed properties such as first sets, follow sets, LR items, etc. +# This data is used for critical parts of the table generation process later. +# ----------------------------------------------------------------------------- + +class GrammarError(YaccError): pass + +class Grammar(object): + def __init__(self,terminals): + self.Productions = [None] # A list of all of the productions. The first + # entry is always reserved for the purpose of + # building an augmented grammar + + self.Prodnames = { } # A dictionary mapping the names of nonterminals to a list of all + # productions of that nonterminal. + + self.Prodmap = { } # A dictionary that is only used to detect duplicate + # productions. + + self.Terminals = { } # A dictionary mapping the names of terminal symbols to a + # list of the rules where they are used. + + for term in terminals: + self.Terminals[term] = [] + + self.Terminals['error'] = [] + + self.Nonterminals = { } # A dictionary mapping names of nonterminals to a list + # of rule numbers where they are used. + + self.First = { } # A dictionary of precomputed FIRST(x) symbols + + self.Follow = { } # A dictionary of precomputed FOLLOW(x) symbols + + self.Precedence = { } # Precedence rules for each terminal. Contains tuples of the + # form ('right',level) or ('nonassoc', level) or ('left',level) + + self.UsedPrecedence = { } # Precedence rules that were actually used by the grammer. + # This is only used to provide error checking and to generate + # a warning about unused precedence rules. + + self.Start = None # Starting symbol for the grammar + + + def __len__(self): + return len(self.Productions) + + def __getitem__(self,index): + return self.Productions[index] + + # ----------------------------------------------------------------------------- + # set_precedence() + # + # Sets the precedence for a given terminal. assoc is the associativity such as + # 'left','right', or 'nonassoc'. level is a numeric level. + # + # ----------------------------------------------------------------------------- + + def set_precedence(self,term,assoc,level): + assert self.Productions == [None],"Must call set_precedence() before add_production()" + if term in self.Precedence: + raise GrammarError("Precedence already specified for terminal '%s'" % term) + if assoc not in ['left','right','nonassoc']: + raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") + self.Precedence[term] = (assoc,level) + + # ----------------------------------------------------------------------------- + # add_production() + # + # Given an action function, this function assembles a production rule and + # computes its precedence level. + # + # The production rule is supplied as a list of symbols. For example, + # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and + # symbols ['expr','PLUS','term']. + # + # Precedence is determined by the precedence of the right-most non-terminal + # or the precedence of a terminal specified by %prec. + # + # A variety of error checks are performed to make sure production symbols + # are valid and that %prec is used correctly. + # ----------------------------------------------------------------------------- + + def add_production(self,prodname,syms,func=None,file='',line=0): + + if prodname in self.Terminals: + raise GrammarError("%s:%d: Illegal rule name '%s'. Already defined as a token" % (file,line,prodname)) + if prodname == 'error': + raise GrammarError("%s:%d: Illegal rule name '%s'. error is a reserved word" % (file,line,prodname)) + if not _is_identifier.match(prodname): + raise GrammarError("%s:%d: Illegal rule name '%s'" % (file,line,prodname)) + + # Look for literal tokens + for n,s in enumerate(syms): + if s[0] in "'\"": + try: + c = eval(s) + if (len(c) > 1): + raise GrammarError("%s:%d: Literal token %s in rule '%s' may only be a single character" % (file,line,s, prodname)) + if not c in self.Terminals: + self.Terminals[c] = [] + syms[n] = c + continue + except SyntaxError: + pass + if not _is_identifier.match(s) and s != '%prec': + raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (file,line,s, prodname)) + + # Determine the precedence level + if '%prec' in syms: + if syms[-1] == '%prec': + raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line)) + if syms[-2] != '%prec': + raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line)) + precname = syms[-1] + prodprec = self.Precedence.get(precname,None) + if not prodprec: + raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname)) + else: + self.UsedPrecedence[precname] = 1 + del syms[-2:] # Drop %prec from the rule + else: + # If no %prec, precedence is determined by the rightmost terminal symbol + precname = rightmost_terminal(syms,self.Terminals) + prodprec = self.Precedence.get(precname,('right',0)) + + # See if the rule is already in the rulemap + map = "%s -> %s" % (prodname,syms) + if map in self.Prodmap: + m = self.Prodmap[map] + raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) + + "Previous definition at %s:%d" % (m.file, m.line)) + + # From this point on, everything is valid. Create a new Production instance + pnumber = len(self.Productions) + if not prodname in self.Nonterminals: + self.Nonterminals[prodname] = [ ] + + # Add the production number to Terminals and Nonterminals + for t in syms: + if t in self.Terminals: + self.Terminals[t].append(pnumber) + else: + if not t in self.Nonterminals: + self.Nonterminals[t] = [ ] + self.Nonterminals[t].append(pnumber) + + # Create a production and add it to the list of productions + p = Production(pnumber,prodname,syms,prodprec,func,file,line) + self.Productions.append(p) + self.Prodmap[map] = p + + # Add to the global productions list + try: + self.Prodnames[prodname].append(p) + except KeyError: + self.Prodnames[prodname] = [ p ] + return 0 + + # ----------------------------------------------------------------------------- + # set_start() + # + # Sets the starting symbol and creates the augmented grammar. Production + # rule 0 is S' -> start where start is the start symbol. + # ----------------------------------------------------------------------------- + + def set_start(self,start=None): + if not start: + start = self.Productions[1].name + if start not in self.Nonterminals: + raise GrammarError("start symbol %s undefined" % start) + self.Productions[0] = Production(0,"S'",[start]) + self.Nonterminals[start].append(0) + self.Start = start + + # ----------------------------------------------------------------------------- + # find_unreachable() + # + # Find all of the nonterminal symbols that can't be reached from the starting + # symbol. Returns a list of nonterminals that can't be reached. + # ----------------------------------------------------------------------------- + + def find_unreachable(self): + + # Mark all symbols that are reachable from a symbol s + def mark_reachable_from(s): + if reachable[s]: + # We've already reached symbol s. + return + reachable[s] = 1 + for p in self.Prodnames.get(s,[]): + for r in p.prod: + mark_reachable_from(r) + + reachable = { } + for s in list(self.Terminals) + list(self.Nonterminals): + reachable[s] = 0 + + mark_reachable_from( self.Productions[0].prod[0] ) + + return [s for s in list(self.Nonterminals) + if not reachable[s]] + + # ----------------------------------------------------------------------------- + # infinite_cycles() + # + # This function looks at the various parsing rules and tries to detect + # infinite recursion cycles (grammar rules where there is no possible way + # to derive a string of only terminals). + # ----------------------------------------------------------------------------- + + def infinite_cycles(self): + terminates = {} + + # Terminals: + for t in self.Terminals: + terminates[t] = 1 + + terminates['$end'] = 1 + + # Nonterminals: + + # Initialize to false: + for n in self.Nonterminals: + terminates[n] = 0 + + # Then propagate termination until no change: + while 1: + some_change = 0 + for (n,pl) in self.Prodnames.items(): + # Nonterminal n terminates iff any of its productions terminates. + for p in pl: + # Production p terminates iff all of its rhs symbols terminate. + for s in p.prod: + if not terminates[s]: + # The symbol s does not terminate, + # so production p does not terminate. + p_terminates = 0 + break + else: + # didn't break from the loop, + # so every symbol s terminates + # so production p terminates. + p_terminates = 1 + + if p_terminates: + # symbol n terminates! + if not terminates[n]: + terminates[n] = 1 + some_change = 1 + # Don't need to consider any more productions for this n. + break + + if not some_change: + break + + infinite = [] + for (s,term) in terminates.items(): + if not term: + if not s in self.Prodnames and not s in self.Terminals and s != 'error': + # s is used-but-not-defined, and we've already warned of that, + # so it would be overkill to say that it's also non-terminating. + pass + else: + infinite.append(s) + + return infinite + + + # ----------------------------------------------------------------------------- + # undefined_symbols() + # + # Find all symbols that were used the grammar, but not defined as tokens or + # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol + # and prod is the production where the symbol was used. + # ----------------------------------------------------------------------------- + def undefined_symbols(self): + result = [] + for p in self.Productions: + if not p: continue + + for s in p.prod: + if not s in self.Prodnames and not s in self.Terminals and s != 'error': + result.append((s,p)) + return result + + # ----------------------------------------------------------------------------- + # unused_terminals() + # + # Find all terminals that were defined, but not used by the grammar. Returns + # a list of all symbols. + # ----------------------------------------------------------------------------- + def unused_terminals(self): + unused_tok = [] + for s,v in self.Terminals.items(): + if s != 'error' and not v: + unused_tok.append(s) + + return unused_tok + + # ------------------------------------------------------------------------------ + # unused_rules() + # + # Find all grammar rules that were defined, but not used (maybe not reachable) + # Returns a list of productions. + # ------------------------------------------------------------------------------ + + def unused_rules(self): + unused_prod = [] + for s,v in self.Nonterminals.items(): + if not v: + p = self.Prodnames[s][0] + unused_prod.append(p) + return unused_prod + + # ----------------------------------------------------------------------------- + # unused_precedence() + # + # Returns a list of tuples (term,precedence) corresponding to precedence + # rules that were never used by the grammar. term is the name of the terminal + # on which precedence was applied and precedence is a string such as 'left' or + # 'right' corresponding to the type of precedence. + # ----------------------------------------------------------------------------- + + def unused_precedence(self): + unused = [] + for termname in self.Precedence: + if not (termname in self.Terminals or termname in self.UsedPrecedence): + unused.append((termname,self.Precedence[termname][0])) + + return unused + + # ------------------------------------------------------------------------- + # _first() + # + # Compute the value of FIRST1(beta) where beta is a tuple of symbols. + # + # During execution of compute_first1, the result may be incomplete. + # Afterward (e.g., when called from compute_follow()), it will be complete. + # ------------------------------------------------------------------------- + def _first(self,beta): + + # We are computing First(x1,x2,x3,...,xn) + result = [ ] + for x in beta: + x_produces_empty = 0 + + # Add all the non- symbols of First[x] to the result. + for f in self.First[x]: + if f == '': + x_produces_empty = 1 + else: + if f not in result: result.append(f) + + if x_produces_empty: + # We have to consider the next x in beta, + # i.e. stay in the loop. + pass + else: + # We don't have to consider any further symbols in beta. + break + else: + # There was no 'break' from the loop, + # so x_produces_empty was true for all x in beta, + # so beta produces empty as well. + result.append('') + + return result + + # ------------------------------------------------------------------------- + # compute_first() + # + # Compute the value of FIRST1(X) for all symbols + # ------------------------------------------------------------------------- + def compute_first(self): + if self.First: + return self.First + + # Terminals: + for t in self.Terminals: + self.First[t] = [t] + + self.First['$end'] = ['$end'] + + # Nonterminals: + + # Initialize to the empty set: + for n in self.Nonterminals: + self.First[n] = [] + + # Then propagate symbols until no change: + while 1: + some_change = 0 + for n in self.Nonterminals: + for p in self.Prodnames[n]: + for f in self._first(p.prod): + if f not in self.First[n]: + self.First[n].append( f ) + some_change = 1 + if not some_change: + break + + return self.First + + # --------------------------------------------------------------------- + # compute_follow() + # + # Computes all of the follow sets for every non-terminal symbol. The + # follow set is the set of all symbols that might follow a given + # non-terminal. See the Dragon book, 2nd Ed. p. 189. + # --------------------------------------------------------------------- + def compute_follow(self,start=None): + # If already computed, return the result + if self.Follow: + return self.Follow + + # If first sets not computed yet, do that first. + if not self.First: + self.compute_first() + + # Add '$end' to the follow list of the start symbol + for k in self.Nonterminals: + self.Follow[k] = [ ] + + if not start: + start = self.Productions[1].name + + self.Follow[start] = [ '$end' ] + + while 1: + didadd = 0 + for p in self.Productions[1:]: + # Here is the production set + for i in range(len(p.prod)): + B = p.prod[i] + if B in self.Nonterminals: + # Okay. We got a non-terminal in a production + fst = self._first(p.prod[i+1:]) + hasempty = 0 + for f in fst: + if f != '' and f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = 1 + if f == '': + hasempty = 1 + if hasempty or i == (len(p.prod)-1): + # Add elements of follow(a) to follow(b) + for f in self.Follow[p.name]: + if f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = 1 + if not didadd: break + return self.Follow + + + # ----------------------------------------------------------------------------- + # build_lritems() + # + # This function walks the list of productions and builds a complete set of the + # LR items. The LR items are stored in two ways: First, they are uniquely + # numbered and placed in the list _lritems. Second, a linked list of LR items + # is built for each production. For example: + # + # E -> E PLUS E + # + # Creates the list + # + # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] + # ----------------------------------------------------------------------------- + + def build_lritems(self): + for p in self.Productions: + lastlri = p + i = 0 + lr_items = [] + while 1: + if i > len(p): + lri = None + else: + lri = LRItem(p,i) + # Precompute the list of productions immediately following + try: + lri.lr_after = self.Prodnames[lri.prod[i+1]] + except (IndexError,KeyError): + lri.lr_after = [] + try: + lri.lr_before = lri.prod[i-1] + except IndexError: + lri.lr_before = None + + lastlri.lr_next = lri + if not lri: break + lr_items.append(lri) + lastlri = lri + i += 1 + p.lr_items = lr_items + +# ----------------------------------------------------------------------------- +# == Class LRTable == +# +# This basic class represents a basic table of LR parsing information. +# Methods for generating the tables are not defined here. They are defined +# in the derived class LRGeneratedTable. +# ----------------------------------------------------------------------------- + +class VersionError(YaccError): pass + +class LRTable(object): + def __init__(self): + self.lr_action = None + self.lr_goto = None + self.lr_productions = None + self.lr_method = None + + def read_table(self,module): + if isinstance(module,types.ModuleType): + parsetab = module + else: + if sys.version_info[0] < 3: + exec("import %s as parsetab" % module) + else: + env = { } + exec("import %s as parsetab" % module, env, env) + parsetab = env['parsetab'] + + if parsetab._tabversion != __tabversion__: + raise VersionError("yacc table file version is out of date") + + self.lr_action = parsetab._lr_action + self.lr_goto = parsetab._lr_goto + + self.lr_productions = [] + for p in parsetab._lr_productions: + self.lr_productions.append(MiniProduction(*p)) + + self.lr_method = parsetab._lr_method + return parsetab._lr_signature + + def read_pickle(self,filename): + try: + import cPickle as pickle + except ImportError: + import pickle + + in_f = open(filename,"rb") + + tabversion = pickle.load(in_f) + if tabversion != __tabversion__: + raise VersionError("yacc table file version is out of date") + self.lr_method = pickle.load(in_f) + signature = pickle.load(in_f) + self.lr_action = pickle.load(in_f) + self.lr_goto = pickle.load(in_f) + productions = pickle.load(in_f) + + self.lr_productions = [] + for p in productions: + self.lr_productions.append(MiniProduction(*p)) + + in_f.close() + return signature + + # Bind all production function names to callable objects in pdict + def bind_callables(self,pdict): + for p in self.lr_productions: + p.bind(pdict) + +# ----------------------------------------------------------------------------- +# === LR Generator === +# +# The following classes and functions are used to generate LR parsing tables on +# a grammar. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# digraph() +# traverse() +# +# The following two functions are used to compute set valued functions +# of the form: +# +# F(x) = F'(x) U U{F(y) | x R y} +# +# This is used to compute the values of Read() sets as well as FOLLOW sets +# in LALR(1) generation. +# +# Inputs: X - An input set +# R - A relation +# FP - Set-valued function +# ------------------------------------------------------------------------------ + +def digraph(X,R,FP): + N = { } + for x in X: + N[x] = 0 + stack = [] + F = { } + for x in X: + if N[x] == 0: traverse(x,N,stack,F,X,R,FP) + return F + +def traverse(x,N,stack,F,X,R,FP): + stack.append(x) + d = len(stack) + N[x] = d + F[x] = FP(x) # F(X) <- F'(x) + + rel = R(x) # Get y's related to x + for y in rel: + if N[y] == 0: + traverse(y,N,stack,F,X,R,FP) + N[x] = min(N[x],N[y]) + for a in F.get(y,[]): + if a not in F[x]: F[x].append(a) + if N[x] == d: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + while element != x: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + +class LALRError(YaccError): pass + +# ----------------------------------------------------------------------------- +# == LRGeneratedTable == +# +# This class implements the LR table generation algorithm. There are no +# public methods except for write() +# ----------------------------------------------------------------------------- + +class LRGeneratedTable(LRTable): + def __init__(self,grammar,method='LALR',log=None): + if method not in ['SLR','LALR']: + raise LALRError("Unsupported method %s" % method) + + self.grammar = grammar + self.lr_method = method + + # Set up the logger + if not log: + log = NullLogger() + self.log = log + + # Internal attributes + self.lr_action = {} # Action table + self.lr_goto = {} # Goto table + self.lr_productions = grammar.Productions # Copy of grammar Production array + self.lr_goto_cache = {} # Cache of computed gotos + self.lr0_cidhash = {} # Cache of closures + + self._add_count = 0 # Internal counter used to detect cycles + + # Diagonistic information filled in by the table generator + self.sr_conflict = 0 + self.rr_conflict = 0 + self.conflicts = [] # List of conflicts + + self.sr_conflicts = [] + self.rr_conflicts = [] + + # Build the tables + self.grammar.build_lritems() + self.grammar.compute_first() + self.grammar.compute_follow() + self.lr_parse_table() + + # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. + + def lr0_closure(self,I): + self._add_count += 1 + + # Add everything in I to J + J = I[:] + didadd = 1 + while didadd: + didadd = 0 + for j in J: + for x in j.lr_after: + if getattr(x,"lr0_added",0) == self._add_count: continue + # Add B --> .G to J + J.append(x.lr_next) + x.lr0_added = self._add_count + didadd = 1 + + return J + + # Compute the LR(0) goto function goto(I,X) where I is a set + # of LR(0) items and X is a grammar symbol. This function is written + # in a way that guarantees uniqueness of the generated goto sets + # (i.e. the same goto set will never be returned as two different Python + # objects). With uniqueness, we can later do fast set comparisons using + # id(obj) instead of element-wise comparison. + + def lr0_goto(self,I,x): + # First we look for a previously cached entry + g = self.lr_goto_cache.get((id(I),x),None) + if g: return g + + # Now we generate the goto set in a way that guarantees uniqueness + # of the result + + s = self.lr_goto_cache.get(x,None) + if not s: + s = { } + self.lr_goto_cache[x] = s + + gs = [ ] + for p in I: + n = p.lr_next + if n and n.lr_before == x: + s1 = s.get(id(n),None) + if not s1: + s1 = { } + s[id(n)] = s1 + gs.append(n) + s = s1 + g = s.get('$end',None) + if not g: + if gs: + g = self.lr0_closure(gs) + s['$end'] = g + else: + s['$end'] = gs + self.lr_goto_cache[(id(I),x)] = g + return g + + # Compute the LR(0) sets of item function + def lr0_items(self): + + C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ] + i = 0 + for I in C: + self.lr0_cidhash[id(I)] = i + i += 1 + + # Loop over the items in C and each grammar symbols + i = 0 + while i < len(C): + I = C[i] + i += 1 + + # Collect all of the symbols that could possibly be in the goto(I,X) sets + asyms = { } + for ii in I: + for s in ii.usyms: + asyms[s] = None + + for x in asyms: + g = self.lr0_goto(I,x) + if not g: continue + if id(g) in self.lr0_cidhash: continue + self.lr0_cidhash[id(g)] = len(C) + C.append(g) + + return C + + # ----------------------------------------------------------------------------- + # ==== LALR(1) Parsing ==== + # + # LALR(1) parsing is almost exactly the same as SLR except that instead of + # relying upon Follow() sets when performing reductions, a more selective + # lookahead set that incorporates the state of the LR(0) machine is utilized. + # Thus, we mainly just have to focus on calculating the lookahead sets. + # + # The method used here is due to DeRemer and Pennelo (1982). + # + # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) + # Lookahead Sets", ACM Transactions on Programming Languages and Systems, + # Vol. 4, No. 4, Oct. 1982, pp. 615-649 + # + # Further details can also be found in: + # + # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", + # McGraw-Hill Book Company, (1985). + # + # ----------------------------------------------------------------------------- + + # ----------------------------------------------------------------------------- + # compute_nullable_nonterminals() + # + # Creates a dictionary containing all of the non-terminals that might produce + # an empty production. + # ----------------------------------------------------------------------------- + + def compute_nullable_nonterminals(self): + nullable = {} + num_nullable = 0 + while 1: + for p in self.grammar.Productions[1:]: + if p.len == 0: + nullable[p.name] = 1 + continue + for t in p.prod: + if not t in nullable: break + else: + nullable[p.name] = 1 + if len(nullable) == num_nullable: break + num_nullable = len(nullable) + return nullable + + # ----------------------------------------------------------------------------- + # find_nonterminal_trans(C) + # + # Given a set of LR(0) items, this functions finds all of the non-terminal + # transitions. These are transitions in which a dot appears immediately before + # a non-terminal. Returns a list of tuples of the form (state,N) where state + # is the state number and N is the nonterminal symbol. + # + # The input C is the set of LR(0) items. + # ----------------------------------------------------------------------------- + + def find_nonterminal_transitions(self,C): + trans = [] + for state in range(len(C)): + for p in C[state]: + if p.lr_index < p.len - 1: + t = (state,p.prod[p.lr_index+1]) + if t[1] in self.grammar.Nonterminals: + if t not in trans: trans.append(t) + state = state + 1 + return trans + + # ----------------------------------------------------------------------------- + # dr_relation() + # + # Computes the DR(p,A) relationships for non-terminal transitions. The input + # is a tuple (state,N) where state is a number and N is a nonterminal symbol. + # + # Returns a list of terminals. + # ----------------------------------------------------------------------------- + + def dr_relation(self,C,trans,nullable): + dr_set = { } + state,N = trans + terms = [] + + g = self.lr0_goto(C[state],N) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index+1] + if a in self.grammar.Terminals: + if a not in terms: terms.append(a) + + # This extra bit is to handle the start state + if state == 0 and N == self.grammar.Productions[0].prod[0]: + terms.append('$end') + + return terms + + # ----------------------------------------------------------------------------- + # reads_relation() + # + # Computes the READS() relation (p,A) READS (t,C). + # ----------------------------------------------------------------------------- + + def reads_relation(self,C, trans, empty): + # Look for empty transitions + rel = [] + state, N = trans + + g = self.lr0_goto(C[state],N) + j = self.lr0_cidhash.get(id(g),-1) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index + 1] + if a in empty: + rel.append((j,a)) + + return rel + + # ----------------------------------------------------------------------------- + # compute_lookback_includes() + # + # Determines the lookback and includes relations + # + # LOOKBACK: + # + # This relation is determined by running the LR(0) state machine forward. + # For example, starting with a production "N : . A B C", we run it forward + # to obtain "N : A B C ." We then build a relationship between this final + # state and the starting state. These relationships are stored in a dictionary + # lookdict. + # + # INCLUDES: + # + # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). + # + # This relation is used to determine non-terminal transitions that occur + # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) + # if the following holds: + # + # B -> LAT, where T -> epsilon and p' -L-> p + # + # L is essentially a prefix (which may be empty), T is a suffix that must be + # able to derive an empty string. State p' must lead to state p with the string L. + # + # ----------------------------------------------------------------------------- + + def compute_lookback_includes(self,C,trans,nullable): + + lookdict = {} # Dictionary of lookback relations + includedict = {} # Dictionary of include relations + + # Make a dictionary of non-terminal transitions + dtrans = {} + for t in trans: + dtrans[t] = 1 + + # Loop over all transitions and compute lookbacks and includes + for state,N in trans: + lookb = [] + includes = [] + for p in C[state]: + if p.name != N: continue + + # Okay, we have a name match. We now follow the production all the way + # through the state machine until we get the . on the right hand side + + lr_index = p.lr_index + j = state + while lr_index < p.len - 1: + lr_index = lr_index + 1 + t = p.prod[lr_index] + + # Check to see if this symbol and state are a non-terminal transition + if (j,t) in dtrans: + # Yes. Okay, there is some chance that this is an includes relation + # the only way to know for certain is whether the rest of the + # production derives empty + + li = lr_index + 1 + while li < p.len: + if p.prod[li] in self.grammar.Terminals: break # No forget it + if not p.prod[li] in nullable: break + li = li + 1 + else: + # Appears to be a relation between (j,t) and (state,N) + includes.append((j,t)) + + g = self.lr0_goto(C[j],t) # Go to next set + j = self.lr0_cidhash.get(id(g),-1) # Go to next state + + # When we get here, j is the final state, now we have to locate the production + for r in C[j]: + if r.name != p.name: continue + if r.len != p.len: continue + i = 0 + # This look is comparing a production ". A B C" with "A B C ." + while i < r.lr_index: + if r.prod[i] != p.prod[i+1]: break + i = i + 1 + else: + lookb.append((j,r)) + for i in includes: + if not i in includedict: includedict[i] = [] + includedict[i].append((state,N)) + lookdict[(state,N)] = lookb + + return lookdict,includedict + + # ----------------------------------------------------------------------------- + # compute_read_sets() + # + # Given a set of LR(0) items, this function computes the read sets. + # + # Inputs: C = Set of LR(0) items + # ntrans = Set of nonterminal transitions + # nullable = Set of empty transitions + # + # Returns a set containing the read sets + # ----------------------------------------------------------------------------- + + def compute_read_sets(self,C, ntrans, nullable): + FP = lambda x: self.dr_relation(C,x,nullable) + R = lambda x: self.reads_relation(C,x,nullable) + F = digraph(ntrans,R,FP) + return F + + # ----------------------------------------------------------------------------- + # compute_follow_sets() + # + # Given a set of LR(0) items, a set of non-terminal transitions, a readset, + # and an include set, this function computes the follow sets + # + # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} + # + # Inputs: + # ntrans = Set of nonterminal transitions + # readsets = Readset (previously computed) + # inclsets = Include sets (previously computed) + # + # Returns a set containing the follow sets + # ----------------------------------------------------------------------------- + + def compute_follow_sets(self,ntrans,readsets,inclsets): + FP = lambda x: readsets[x] + R = lambda x: inclsets.get(x,[]) + F = digraph(ntrans,R,FP) + return F + + # ----------------------------------------------------------------------------- + # add_lookaheads() + # + # Attaches the lookahead symbols to grammar rules. + # + # Inputs: lookbacks - Set of lookback relations + # followset - Computed follow set + # + # This function directly attaches the lookaheads to productions contained + # in the lookbacks set + # ----------------------------------------------------------------------------- + + def add_lookaheads(self,lookbacks,followset): + for trans,lb in lookbacks.items(): + # Loop over productions in lookback + for state,p in lb: + if not state in p.lookaheads: + p.lookaheads[state] = [] + f = followset.get(trans,[]) + for a in f: + if a not in p.lookaheads[state]: p.lookaheads[state].append(a) + + # ----------------------------------------------------------------------------- + # add_lalr_lookaheads() + # + # This function does all of the work of adding lookahead information for use + # with LALR parsing + # ----------------------------------------------------------------------------- + + def add_lalr_lookaheads(self,C): + # Determine all of the nullable nonterminals + nullable = self.compute_nullable_nonterminals() + + # Find all non-terminal transitions + trans = self.find_nonterminal_transitions(C) + + # Compute read sets + readsets = self.compute_read_sets(C,trans,nullable) + + # Compute lookback/includes relations + lookd, included = self.compute_lookback_includes(C,trans,nullable) + + # Compute LALR FOLLOW sets + followsets = self.compute_follow_sets(trans,readsets,included) + + # Add all of the lookaheads + self.add_lookaheads(lookd,followsets) + + # ----------------------------------------------------------------------------- + # lr_parse_table() + # + # This function constructs the parse tables for SLR or LALR + # ----------------------------------------------------------------------------- + def lr_parse_table(self): + Productions = self.grammar.Productions + Precedence = self.grammar.Precedence + goto = self.lr_goto # Goto array + action = self.lr_action # Action array + log = self.log # Logger for output + + actionp = { } # Action production array (temporary) + + log.info("Parsing method: %s", self.lr_method) + + # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items + # This determines the number of states + + C = self.lr0_items() + + if self.lr_method == 'LALR': + self.add_lalr_lookaheads(C) + + # Build the parser table, state by state + st = 0 + for I in C: + # Loop over each production in I + actlist = [ ] # List of actions + st_action = { } + st_actionp = { } + st_goto = { } + log.info("") + log.info("state %d", st) + log.info("") + for p in I: + log.info(" (%d) %s", p.number, str(p)) + log.info("") + + for p in I: + if p.len == p.lr_index + 1: + if p.name == "S'": + # Start symbol. Accept! + st_action["$end"] = 0 + st_actionp["$end"] = p + else: + # We are at the end of a production. Reduce! + if self.lr_method == 'LALR': + laheads = p.lookaheads[st] + else: + laheads = self.grammar.Follow[p.name] + for a in laheads: + actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p))) + r = st_action.get(a,None) + if r is not None: + # Whoa. Have a shift/reduce or reduce/reduce conflict + if r > 0: + # Need to decide on shift or reduce here + # By default we favor shifting. Need to add + # some precedence rules here. + sprec,slevel = Productions[st_actionp[a].number].prec + rprec,rlevel = Precedence.get(a,('right',0)) + if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): + # We really need to reduce here. + st_action[a] = -p.number + st_actionp[a] = p + if not slevel and not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as reduce",a) + self.sr_conflicts.append((st,a,'reduce')) + Productions[p.number].reduced += 1 + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the shift + if not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as shift",a) + self.sr_conflicts.append((st,a,'shift')) + elif r < 0: + # Reduce/reduce conflict. In this case, we favor the rule + # that was defined first in the grammar file + oldp = Productions[-r] + pp = Productions[p.number] + if oldp.line > pp.line: + st_action[a] = -p.number + st_actionp[a] = p + chosenp,rejectp = pp,oldp + Productions[p.number].reduced += 1 + Productions[oldp.number].reduced -= 1 + else: + chosenp,rejectp = oldp,pp + self.rr_conflicts.append((st,chosenp,rejectp)) + log.info(" ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a]) + else: + raise LALRError("Unknown conflict in state %d" % st) + else: + st_action[a] = -p.number + st_actionp[a] = p + Productions[p.number].reduced += 1 + else: + i = p.lr_index + a = p.prod[i+1] # Get symbol right after the "." + if a in self.grammar.Terminals: + g = self.lr0_goto(I,a) + j = self.lr0_cidhash.get(id(g),-1) + if j >= 0: + # We are in a shift state + actlist.append((a,p,"shift and go to state %d" % j)) + r = st_action.get(a,None) + if r is not None: + # Whoa have a shift/reduce or shift/shift conflict + if r > 0: + if r != j: + raise LALRError("Shift/shift conflict in state %d" % st) + elif r < 0: + # Do a precedence check. + # - if precedence of reduce rule is higher, we reduce. + # - if precedence of reduce is same and left assoc, we reduce. + # - otherwise we shift + rprec,rlevel = Productions[st_actionp[a].number].prec + sprec,slevel = Precedence.get(a,('right',0)) + if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): + # We decide to shift here... highest precedence to shift + Productions[st_actionp[a].number].reduced -= 1 + st_action[a] = j + st_actionp[a] = p + if not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as shift",a) + self.sr_conflicts.append((st,a,'shift')) + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the reduce + if not slevel and not rlevel: + log.info(" ! shift/reduce conflict for %s resolved as reduce",a) + self.sr_conflicts.append((st,a,'reduce')) + + else: + raise LALRError("Unknown conflict in state %d" % st) + else: + st_action[a] = j + st_actionp[a] = p + + # Print the actions associated with each terminal + _actprint = { } + for a,p,m in actlist: + if a in st_action: + if p is st_actionp[a]: + log.info(" %-15s %s",a,m) + _actprint[(a,m)] = 1 + log.info("") + # Print the actions that were not used. (debugging) + not_used = 0 + for a,p,m in actlist: + if a in st_action: + if p is not st_actionp[a]: + if not (a,m) in _actprint: + log.debug(" ! %-15s [ %s ]",a,m) + not_used = 1 + _actprint[(a,m)] = 1 + if not_used: + log.debug("") + + # Construct the goto table for this state + + nkeys = { } + for ii in I: + for s in ii.usyms: + if s in self.grammar.Nonterminals: + nkeys[s] = None + for n in nkeys: + g = self.lr0_goto(I,n) + j = self.lr0_cidhash.get(id(g),-1) + if j >= 0: + st_goto[n] = j + log.info(" %-30s shift and go to state %d",n,j) + + action[st] = st_action + actionp[st] = st_actionp + goto[st] = st_goto + st += 1 + + + # ----------------------------------------------------------------------------- + # write() + # + # This function writes the LR parsing tables to a file + # ----------------------------------------------------------------------------- + + def write_table(self,modulename,outputdir='',signature=""): + basemodulename = modulename.split(".")[-1] + filename = os.path.join(outputdir,basemodulename) + ".py" + try: + f = open(filename,"w") + + f.write(""" +# %s +# This file is automatically generated. Do not edit. +_tabversion = %r + +_lr_method = %r + +_lr_signature = %r + """ % (filename, __tabversion__, self.lr_method, signature)) + + # Change smaller to 0 to go back to original tables + smaller = 1 + + # Factor out names to try and make smaller + if smaller: + items = { } + + for s,nd in self.lr_action.items(): + for name,v in nd.items(): + i = items.get(name) + if not i: + i = ([],[]) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write("\n_lr_action_items = {") + for k,v in items.items(): + f.write("%r:([" % k) + for i in v[0]: + f.write("%r," % i) + f.write("],[") + for i in v[1]: + f.write("%r," % i) + + f.write("]),") + f.write("}\n") + + f.write(""" +_lr_action = { } +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = { } + _lr_action[_x][_k] = _y +del _lr_action_items +""") + + else: + f.write("\n_lr_action = { "); + for k,v in self.lr_action.items(): + f.write("(%r,%r):%r," % (k[0],k[1],v)) + f.write("}\n"); + + if smaller: + # Factor out names to try and make smaller + items = { } + + for s,nd in self.lr_goto.items(): + for name,v in nd.items(): + i = items.get(name) + if not i: + i = ([],[]) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write("\n_lr_goto_items = {") + for k,v in items.items(): + f.write("%r:([" % k) + for i in v[0]: + f.write("%r," % i) + f.write("],[") + for i in v[1]: + f.write("%r," % i) + + f.write("]),") + f.write("}\n") + + f.write(""" +_lr_goto = { } +for _k, _v in _lr_goto_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_goto: _lr_goto[_x] = { } + _lr_goto[_x][_k] = _y +del _lr_goto_items +""") + else: + f.write("\n_lr_goto = { "); + for k,v in self.lr_goto.items(): + f.write("(%r,%r):%r," % (k[0],k[1],v)) + f.write("}\n"); + + # Write production table + f.write("_lr_productions = [\n") + for p in self.lr_productions: + if p.func: + f.write(" (%r,%r,%d,%r,%r,%d),\n" % (p.str,p.name, p.len, p.func,p.file,p.line)) + else: + f.write(" (%r,%r,%d,None,None,None),\n" % (str(p),p.name, p.len)) + f.write("]\n") + f.close() + + except IOError: + e = sys.exc_info()[1] + sys.stderr.write("Unable to create '%s'\n" % filename) + sys.stderr.write(str(e)+"\n") + return + + + # ----------------------------------------------------------------------------- + # pickle_table() + # + # This function pickles the LR parsing tables to a supplied file object + # ----------------------------------------------------------------------------- + + def pickle_table(self,filename,signature=""): + try: + import cPickle as pickle + except ImportError: + import pickle + outf = open(filename,"wb") + pickle.dump(__tabversion__,outf,pickle_protocol) + pickle.dump(self.lr_method,outf,pickle_protocol) + pickle.dump(signature,outf,pickle_protocol) + pickle.dump(self.lr_action,outf,pickle_protocol) + pickle.dump(self.lr_goto,outf,pickle_protocol) + + outp = [] + for p in self.lr_productions: + if p.func: + outp.append((p.str,p.name, p.len, p.func,p.file,p.line)) + else: + outp.append((str(p),p.name,p.len,None,None,None)) + pickle.dump(outp,outf,pickle_protocol) + outf.close() + +# ----------------------------------------------------------------------------- +# === INTROSPECTION === +# +# The following functions and classes are used to implement the PLY +# introspection features followed by the yacc() function itself. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + try: + raise RuntimeError + except RuntimeError: + e,b,t = sys.exc_info() + f = t.tb_frame + while levels > 0: + f = f.f_back + levels -= 1 + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + + return ldict + +# ----------------------------------------------------------------------------- +# parse_grammar() +# +# This takes a raw grammar rule string and parses it into production data +# ----------------------------------------------------------------------------- +def parse_grammar(doc,file,line): + grammar = [] + # Split the doc string into lines + pstrings = doc.splitlines() + lastp = None + dline = line + for ps in pstrings: + dline += 1 + p = ps.split() + if not p: continue + try: + if p[0] == '|': + # This is a continuation of a previous rule + if not lastp: + raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline)) + prodname = lastp + syms = p[1:] + else: + prodname = p[0] + lastp = prodname + syms = p[2:] + assign = p[1] + if assign != ':' and assign != '::=': + raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline)) + + grammar.append((file,dline,prodname,syms)) + except SyntaxError: + raise + except Exception: + raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip())) + + return grammar + +# ----------------------------------------------------------------------------- +# ParserReflect() +# +# This class represents information extracted for building a parser including +# start symbol, error function, tokens, precedence list, action functions, +# etc. +# ----------------------------------------------------------------------------- +class ParserReflect(object): + def __init__(self,pdict,log=None): + self.pdict = pdict + self.start = None + self.error_func = None + self.tokens = None + self.files = {} + self.grammar = [] + self.error = 0 + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_start() + self.get_error_func() + self.get_tokens() + self.get_precedence() + self.get_pfunctions() + + # Validate all of the information + def validate_all(self): + self.validate_start() + self.validate_error_func() + self.validate_tokens() + self.validate_precedence() + self.validate_pfunctions() + self.validate_files() + return self.error + + # Compute a signature over the grammar + def signature(self): + try: + from hashlib import md5 + except ImportError: + from md5 import md5 + try: + sig = md5() + if self.start: + sig.update(self.start.encode('latin-1')) + if self.prec: + sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1')) + if self.tokens: + sig.update(" ".join(self.tokens).encode('latin-1')) + for f in self.pfuncs: + if f[3]: + sig.update(f[3].encode('latin-1')) + except (TypeError,ValueError): + pass + return sig.digest() + + # ----------------------------------------------------------------------------- + # validate_file() + # + # This method checks to see if there are duplicated p_rulename() functions + # in the parser module file. Without this function, it is really easy for + # users to make mistakes by cutting and pasting code fragments (and it's a real + # bugger to try and figure out why the resulting parser doesn't work). Therefore, + # we just do a little regular expression pattern matching of def statements + # to try and detect duplicates. + # ----------------------------------------------------------------------------- + + def validate_files(self): + # Match def p_funcname( + fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') + + for filename in self.files.keys(): + base,ext = os.path.splitext(filename) + if ext != '.py': return 1 # No idea. Assume it's okay. + + try: + f = open(filename) + lines = f.readlines() + f.close() + except IOError: + continue + + counthash = { } + for linen,l in enumerate(lines): + linen += 1 + m = fre.match(l) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev) + + # Get the start symbol + def get_start(self): + self.start = self.pdict.get('start') + + # Validate the start symbol + def validate_start(self): + if self.start is not None: + if not isinstance(self.start,str): + self.log.error("'start' must be a string") + + # Look for error handler + def get_error_func(self): + self.error_func = self.pdict.get('p_error') + + # Validate the error function + def validate_error_func(self): + if self.error_func: + if isinstance(self.error_func,types.FunctionType): + ismethod = 0 + elif isinstance(self.error_func, types.MethodType): + ismethod = 1 + else: + self.log.error("'p_error' defined, but is not a function or method") + self.error = 1 + return + + eline = func_code(self.error_func).co_firstlineno + efile = func_code(self.error_func).co_filename + self.files[efile] = 1 + + if (func_code(self.error_func).co_argcount != 1+ismethod): + self.log.error("%s:%d: p_error() requires 1 argument",efile,eline) + self.error = 1 + + # Get the tokens map + def get_tokens(self): + tokens = self.pdict.get("tokens",None) + if not tokens: + self.log.error("No token list is defined") + self.error = 1 + return + + if not isinstance(tokens,(list, tuple)): + self.log.error("tokens must be a list or tuple") + self.error = 1 + return + + if not tokens: + self.log.error("tokens is empty") + self.error = 1 + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + # Validate the tokens. + if 'error' in self.tokens: + self.log.error("Illegal token name 'error'. Is a reserved word") + self.error = 1 + return + + terminals = {} + for n in self.tokens: + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the precedence map (if any) + def get_precedence(self): + self.prec = self.pdict.get("precedence",None) + + # Validate and parse the precedence map + def validate_precedence(self): + preclist = [] + if self.prec: + if not isinstance(self.prec,(list,tuple)): + self.log.error("precedence must be a list or tuple") + self.error = 1 + return + for level,p in enumerate(self.prec): + if not isinstance(p,(list,tuple)): + self.log.error("Bad precedence table") + self.error = 1 + return + + if len(p) < 2: + self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p) + self.error = 1 + return + assoc = p[0] + if not isinstance(assoc,str): + self.log.error("precedence associativity must be a string") + self.error = 1 + return + for term in p[1:]: + if not isinstance(term,str): + self.log.error("precedence items must be strings") + self.error = 1 + return + preclist.append((term,assoc,level+1)) + self.preclist = preclist + + # Get all p_functions from the grammar + def get_pfunctions(self): + p_functions = [] + for name, item in self.pdict.items(): + if name[:2] != 'p_': continue + if name == 'p_error': continue + if isinstance(item,(types.FunctionType,types.MethodType)): + line = func_code(item).co_firstlineno + file = func_code(item).co_filename + p_functions.append((line,file,name,item.__doc__)) + + # Sort all of the actions by line number + p_functions.sort() + self.pfuncs = p_functions + + + # Validate all of the p_functions + def validate_pfunctions(self): + grammar = [] + # Check for non-empty symbols + if len(self.pfuncs) == 0: + self.log.error("no rules of the form p_rulename are defined") + self.error = 1 + return + + for line, file, name, doc in self.pfuncs: + func = self.pdict[name] + if isinstance(func, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + if func_code(func).co_argcount > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,func.__name__) + self.error = 1 + elif func_code(func).co_argcount < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument",file,line,func.__name__) + self.error = 1 + elif not func.__doc__: + self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__) + else: + try: + parsed_g = parse_grammar(doc,file,line) + for g in parsed_g: + grammar.append((name, g)) + except SyntaxError: + e = sys.exc_info()[1] + self.log.error(str(e)) + self.error = 1 + + # Looks like a valid grammar rule + # Mark the file in which defined. + self.files[file] = 1 + + # Secondary validation step that looks for p_ definitions that are not functions + # or functions that look like they might be grammar rules. + + for n,v in self.pdict.items(): + if n[0:2] == 'p_' and isinstance(v, (types.FunctionType, types.MethodType)): continue + if n[0:2] == 't_': continue + if n[0:2] == 'p_' and n != 'p_error': + self.log.warning("'%s' not defined as a function", n) + if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or + (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)): + try: + doc = v.__doc__.split(" ") + if doc[1] == ':': + self.log.warning("%s:%d: Possible grammar rule '%s' defined without p_ prefix", + func_code(v).co_filename, func_code(v).co_firstlineno,n) + except Exception: + pass + + self.grammar = grammar + +# ----------------------------------------------------------------------------- +# yacc(module) +# +# Build a parser +# ----------------------------------------------------------------------------- + +def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, + check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='', + debuglog=None, errorlog = None, picklefile=None): + + global parse # Reference to the parsing method of the last built parser + + # If pickling is enabled, table files are not created + + if picklefile: + write_tables = 0 + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the parser + if module: + _items = [(k,getattr(module,k)) for k in dir(module)] + pdict = dict(_items) + else: + pdict = get_caller_module_dict(2) + + # Collect parser information from the dictionary + pinfo = ParserReflect(pdict,log=errorlog) + pinfo.get_all() + + if pinfo.error: + raise YaccError("Unable to build parser") + + # Check signature against table files (if any) + signature = pinfo.signature() + + # Read the tables + try: + lr = LRTable() + if picklefile: + read_signature = lr.read_pickle(picklefile) + else: + read_signature = lr.read_table(tabmodule) + if optimize or (read_signature == signature): + try: + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr,pinfo.error_func) + parse = parser.parse + return parser + except Exception: + e = sys.exc_info()[1] + errorlog.warning("There was a problem loading the table file: %s", repr(e)) + except VersionError: + e = sys.exc_info() + errorlog.warning(str(e)) + except Exception: + pass + + if debuglog is None: + if debug: + debuglog = PlyLogger(open(debugfile,"w")) + else: + debuglog = NullLogger() + + debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__) + + + errors = 0 + + # Validate the parser information + if pinfo.validate_all(): + raise YaccError("Unable to build parser") + + if not pinfo.error_func: + errorlog.warning("no p_error() function is defined") + + # Create a grammar object + grammar = Grammar(pinfo.tokens) + + # Set precedence level for terminals + for term, assoc, level in pinfo.preclist: + try: + grammar.set_precedence(term,assoc,level) + except GrammarError: + e = sys.exc_info()[1] + errorlog.warning("%s",str(e)) + + # Add productions to the grammar + for funcname, gram in pinfo.grammar: + file, line, prodname, syms = gram + try: + grammar.add_production(prodname,syms,funcname,file,line) + except GrammarError: + e = sys.exc_info()[1] + errorlog.error("%s",str(e)) + errors = 1 + + # Set the grammar start symbols + try: + if start is None: + grammar.set_start(pinfo.start) + else: + grammar.set_start(start) + except GrammarError: + e = sys.exc_info()[1] + errorlog.error(str(e)) + errors = 1 + + if errors: + raise YaccError("Unable to build parser") + + # Verify the grammar structure + undefined_symbols = grammar.undefined_symbols() + for sym, prod in undefined_symbols: + errorlog.error("%s:%d: Symbol '%s' used, but not defined as a token or a rule",prod.file,prod.line,sym) + errors = 1 + + unused_terminals = grammar.unused_terminals() + if unused_terminals: + debuglog.info("") + debuglog.info("Unused terminals:") + debuglog.info("") + for term in unused_terminals: + errorlog.warning("Token '%s' defined, but not used", term) + debuglog.info(" %s", term) + + # Print out all productions to the debug log + if debug: + debuglog.info("") + debuglog.info("Grammar") + debuglog.info("") + for n,p in enumerate(grammar.Productions): + debuglog.info("Rule %-5d %s", n, p) + + # Find unused non-terminals + unused_rules = grammar.unused_rules() + for prod in unused_rules: + errorlog.warning("%s:%d: Rule '%s' defined, but not used", prod.file, prod.line, prod.name) + + if len(unused_terminals) == 1: + errorlog.warning("There is 1 unused token") + if len(unused_terminals) > 1: + errorlog.warning("There are %d unused tokens", len(unused_terminals)) + + if len(unused_rules) == 1: + errorlog.warning("There is 1 unused rule") + if len(unused_rules) > 1: + errorlog.warning("There are %d unused rules", len(unused_rules)) + + if debug: + debuglog.info("") + debuglog.info("Terminals, with rules where they appear") + debuglog.info("") + terms = list(grammar.Terminals) + terms.sort() + for term in terms: + debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]])) + + debuglog.info("") + debuglog.info("Nonterminals, with rules where they appear") + debuglog.info("") + nonterms = list(grammar.Nonterminals) + nonterms.sort() + for nonterm in nonterms: + debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]])) + debuglog.info("") + + if check_recursion: + unreachable = grammar.find_unreachable() + for u in unreachable: + errorlog.warning("Symbol '%s' is unreachable",u) + + infinite = grammar.infinite_cycles() + for inf in infinite: + errorlog.error("Infinite recursion detected for symbol '%s'", inf) + errors = 1 + + unused_prec = grammar.unused_precedence() + for term, assoc in unused_prec: + errorlog.error("Precedence rule '%s' defined for unknown symbol '%s'", assoc, term) + errors = 1 + + if errors: + raise YaccError("Unable to build parser") + + # Run the LRGeneratedTable on the grammar + if debug: + errorlog.debug("Generating %s tables", method) + + lr = LRGeneratedTable(grammar,method,debuglog) + + if debug: + num_sr = len(lr.sr_conflicts) + + # Report shift/reduce and reduce/reduce conflicts + if num_sr == 1: + errorlog.warning("1 shift/reduce conflict") + elif num_sr > 1: + errorlog.warning("%d shift/reduce conflicts", num_sr) + + num_rr = len(lr.rr_conflicts) + if num_rr == 1: + errorlog.warning("1 reduce/reduce conflict") + elif num_rr > 1: + errorlog.warning("%d reduce/reduce conflicts", num_rr) + + # Write out conflicts to the output file + if debug and (lr.sr_conflicts or lr.rr_conflicts): + debuglog.warning("") + debuglog.warning("Conflicts:") + debuglog.warning("") + + for state, tok, resolution in lr.sr_conflicts: + debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s", tok, state, resolution) + + already_reported = {} + for state, rule, rejected in lr.rr_conflicts: + if (state,id(rule),id(rejected)) in already_reported: + continue + debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) + debuglog.warning("rejected rule (%s) in state %d", rejected,state) + errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) + errorlog.warning("rejected rule (%s) in state %d", rejected, state) + already_reported[state,id(rule),id(rejected)] = 1 + + warned_never = [] + for state, rule, rejected in lr.rr_conflicts: + if not rejected.reduced and (rejected not in warned_never): + debuglog.warning("Rule (%s) is never reduced", rejected) + errorlog.warning("Rule (%s) is never reduced", rejected) + warned_never.append(rejected) + + # Write the table file if requested + if write_tables: + lr.write_table(tabmodule,outputdir,signature) + + # Write a pickled version of the tables + if picklefile: + lr.pickle_table(picklefile,signature) + + # Build the parser + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr,pinfo.error_func) + + parse = parser.parse + return parser diff --git a/engine/src/flutter/third_party/pymock/LICENSE.txt b/engine/src/flutter/third_party/pymock/LICENSE.txt new file mode 100644 index 0000000000..7891703b13 --- /dev/null +++ b/engine/src/flutter/third_party/pymock/LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2003-2012, Michael Foord +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/engine/src/flutter/third_party/pymock/OWNERS b/engine/src/flutter/third_party/pymock/OWNERS new file mode 100644 index 0000000000..9946560376 --- /dev/null +++ b/engine/src/flutter/third_party/pymock/OWNERS @@ -0,0 +1 @@ +sbc@chromium.org diff --git a/engine/src/flutter/third_party/pymock/README.chromium b/engine/src/flutter/third_party/pymock/README.chromium new file mode 100644 index 0000000000..aaf355d79b --- /dev/null +++ b/engine/src/flutter/third_party/pymock/README.chromium @@ -0,0 +1,10 @@ +Name: mock +URL: http://pypi.python.org/pypi/mock +Version: 1.0.1 +Security Critical: no +License: BSD +License File: LICENSE.txt +Description: +Python mock library, currently used by native_client_sdk. This is the +same mock library that is now part of python 3.3 where it is know as +unittest.mock. diff --git a/engine/src/flutter/third_party/pymock/mock.py b/engine/src/flutter/third_party/pymock/mock.py new file mode 100644 index 0000000000..c8fc5d1d25 --- /dev/null +++ b/engine/src/flutter/third_party/pymock/mock.py @@ -0,0 +1,2367 @@ +# mock.py +# Test tools for mocking and patching. +# Copyright (C) 2007-2012 Michael Foord & the mock team +# E-mail: fuzzyman AT voidspace DOT org DOT uk + +# mock 1.0 +# http://www.voidspace.org.uk/python/mock/ + +# Released subject to the BSD License +# Please see http://www.voidspace.org.uk/python/license.shtml + +# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml +# Comments, suggestions and bug reports welcome. + + +__all__ = ( + 'Mock', + 'MagicMock', + 'patch', + 'sentinel', + 'DEFAULT', + 'ANY', + 'call', + 'create_autospec', + 'FILTER_DIR', + 'NonCallableMock', + 'NonCallableMagicMock', + 'mock_open', + 'PropertyMock', +) + + +__version__ = '1.0.1' + + +import pprint +import sys + +try: + import inspect +except ImportError: + # for alternative platforms that + # may not have inspect + inspect = None + +try: + from functools import wraps as original_wraps +except ImportError: + # Python 2.4 compatibility + def wraps(original): + def inner(f): + f.__name__ = original.__name__ + f.__doc__ = original.__doc__ + f.__module__ = original.__module__ + f.__wrapped__ = original + return f + return inner +else: + if sys.version_info[:2] >= (3, 3): + wraps = original_wraps + else: + def wraps(func): + def inner(f): + f = original_wraps(func)(f) + f.__wrapped__ = func + return f + return inner + +try: + unicode +except NameError: + # Python 3 + basestring = unicode = str + +try: + long +except NameError: + # Python 3 + long = int + +try: + BaseException +except NameError: + # Python 2.4 compatibility + BaseException = Exception + +try: + next +except NameError: + def next(obj): + return obj.next() + + +BaseExceptions = (BaseException,) +if 'java' in sys.platform: + # jython + import java + BaseExceptions = (BaseException, java.lang.Throwable) + +try: + _isidentifier = str.isidentifier +except AttributeError: + # Python 2.X + import keyword + import re + regex = re.compile(r'^[a-z_][a-z0-9_]*$', re.I) + def _isidentifier(string): + if string in keyword.kwlist: + return False + return regex.match(string) + + +inPy3k = sys.version_info[0] == 3 + +# Needed to work around Python 3 bug where use of "super" interferes with +# defining __class__ as a descriptor +_super = super + +self = 'im_self' +builtin = '__builtin__' +if inPy3k: + self = '__self__' + builtin = 'builtins' + +FILTER_DIR = True + + +def _is_instance_mock(obj): + # can't use isinstance on Mock objects because they override __class__ + # The base class for all mocks is NonCallableMock + return issubclass(type(obj), NonCallableMock) + + +def _is_exception(obj): + return ( + isinstance(obj, BaseExceptions) or + isinstance(obj, ClassTypes) and issubclass(obj, BaseExceptions) + ) + + +class _slotted(object): + __slots__ = ['a'] + + +DescriptorTypes = ( + type(_slotted.a), + property, +) + + +def _getsignature(func, skipfirst, instance=False): + if inspect is None: + raise ImportError('inspect module not available') + + if isinstance(func, ClassTypes) and not instance: + try: + func = func.__init__ + except AttributeError: + return + skipfirst = True + elif not isinstance(func, FunctionTypes): + # for classes where instance is True we end up here too + try: + func = func.__call__ + except AttributeError: + return + + if inPy3k: + try: + argspec = inspect.getfullargspec(func) + except TypeError: + # C function / method, possibly inherited object().__init__ + return + regargs, varargs, varkw, defaults, kwonly, kwonlydef, ann = argspec + else: + try: + regargs, varargs, varkwargs, defaults = inspect.getargspec(func) + except TypeError: + # C function / method, possibly inherited object().__init__ + return + + # instance methods and classmethods need to lose the self argument + if getattr(func, self, None) is not None: + regargs = regargs[1:] + if skipfirst: + # this condition and the above one are never both True - why? + regargs = regargs[1:] + + if inPy3k: + signature = inspect.formatargspec( + regargs, varargs, varkw, defaults, + kwonly, kwonlydef, ann, formatvalue=lambda value: "") + else: + signature = inspect.formatargspec( + regargs, varargs, varkwargs, defaults, + formatvalue=lambda value: "") + return signature[1:-1], func + + +def _check_signature(func, mock, skipfirst, instance=False): + if not _callable(func): + return + + result = _getsignature(func, skipfirst, instance) + if result is None: + return + signature, func = result + + # can't use self because "self" is common as an argument name + # unfortunately even not in the first place + src = "lambda _mock_self, %s: None" % signature + checksig = eval(src, {}) + _copy_func_details(func, checksig) + type(mock)._mock_check_sig = checksig + + +def _copy_func_details(func, funcopy): + funcopy.__name__ = func.__name__ + funcopy.__doc__ = func.__doc__ + #funcopy.__dict__.update(func.__dict__) + funcopy.__module__ = func.__module__ + if not inPy3k: + funcopy.func_defaults = func.func_defaults + return + funcopy.__defaults__ = func.__defaults__ + funcopy.__kwdefaults__ = func.__kwdefaults__ + + +def _callable(obj): + if isinstance(obj, ClassTypes): + return True + if getattr(obj, '__call__', None) is not None: + return True + return False + + +def _is_list(obj): + # checks for list or tuples + # XXXX badly named! + return type(obj) in (list, tuple) + + +def _instance_callable(obj): + """Given an object, return True if the object is callable. + For classes, return True if instances would be callable.""" + if not isinstance(obj, ClassTypes): + # already an instance + return getattr(obj, '__call__', None) is not None + + klass = obj + # uses __bases__ instead of __mro__ so that we work with old style classes + if klass.__dict__.get('__call__') is not None: + return True + + for base in klass.__bases__: + if _instance_callable(base): + return True + return False + + +def _set_signature(mock, original, instance=False): + # creates a function with signature (*args, **kwargs) that delegates to a + # mock. It still does signature checking by calling a lambda with the same + # signature as the original. + if not _callable(original): + return + + skipfirst = isinstance(original, ClassTypes) + result = _getsignature(original, skipfirst, instance) + if result is None: + # was a C function (e.g. object().__init__ ) that can't be mocked + return + + signature, func = result + + src = "lambda %s: None" % signature + checksig = eval(src, {}) + _copy_func_details(func, checksig) + + name = original.__name__ + if not _isidentifier(name): + name = 'funcopy' + context = {'_checksig_': checksig, 'mock': mock} + src = """def %s(*args, **kwargs): + _checksig_(*args, **kwargs) + return mock(*args, **kwargs)""" % name + exec (src, context) + funcopy = context[name] + _setup_func(funcopy, mock) + return funcopy + + +def _setup_func(funcopy, mock): + funcopy.mock = mock + + # can't use isinstance with mocks + if not _is_instance_mock(mock): + return + + def assert_called_with(*args, **kwargs): + return mock.assert_called_with(*args, **kwargs) + def assert_called_once_with(*args, **kwargs): + return mock.assert_called_once_with(*args, **kwargs) + def assert_has_calls(*args, **kwargs): + return mock.assert_has_calls(*args, **kwargs) + def assert_any_call(*args, **kwargs): + return mock.assert_any_call(*args, **kwargs) + def reset_mock(): + funcopy.method_calls = _CallList() + funcopy.mock_calls = _CallList() + mock.reset_mock() + ret = funcopy.return_value + if _is_instance_mock(ret) and not ret is mock: + ret.reset_mock() + + funcopy.called = False + funcopy.call_count = 0 + funcopy.call_args = None + funcopy.call_args_list = _CallList() + funcopy.method_calls = _CallList() + funcopy.mock_calls = _CallList() + + funcopy.return_value = mock.return_value + funcopy.side_effect = mock.side_effect + funcopy._mock_children = mock._mock_children + + funcopy.assert_called_with = assert_called_with + funcopy.assert_called_once_with = assert_called_once_with + funcopy.assert_has_calls = assert_has_calls + funcopy.assert_any_call = assert_any_call + funcopy.reset_mock = reset_mock + + mock._mock_delegate = funcopy + + +def _is_magic(name): + return '__%s__' % name[2:-2] == name + + +class _SentinelObject(object): + "A unique, named, sentinel object." + def __init__(self, name): + self.name = name + + def __repr__(self): + return 'sentinel.%s' % self.name + + +class _Sentinel(object): + """Access attributes to return a named object, usable as a sentinel.""" + def __init__(self): + self._sentinels = {} + + def __getattr__(self, name): + if name == '__bases__': + # Without this help(mock) raises an exception + raise AttributeError + return self._sentinels.setdefault(name, _SentinelObject(name)) + + +sentinel = _Sentinel() + +DEFAULT = sentinel.DEFAULT +_missing = sentinel.MISSING +_deleted = sentinel.DELETED + + +class OldStyleClass: + pass +ClassType = type(OldStyleClass) + + +def _copy(value): + if type(value) in (dict, list, tuple, set): + return type(value)(value) + return value + + +ClassTypes = (type,) +if not inPy3k: + ClassTypes = (type, ClassType) + +_allowed_names = set( + [ + 'return_value', '_mock_return_value', 'side_effect', + '_mock_side_effect', '_mock_parent', '_mock_new_parent', + '_mock_name', '_mock_new_name' + ] +) + + +def _delegating_property(name): + _allowed_names.add(name) + _the_name = '_mock_' + name + def _get(self, name=name, _the_name=_the_name): + sig = self._mock_delegate + if sig is None: + return getattr(self, _the_name) + return getattr(sig, name) + def _set(self, value, name=name, _the_name=_the_name): + sig = self._mock_delegate + if sig is None: + self.__dict__[_the_name] = value + else: + setattr(sig, name, value) + + return property(_get, _set) + + + +class _CallList(list): + + def __contains__(self, value): + if not isinstance(value, list): + return list.__contains__(self, value) + len_value = len(value) + len_self = len(self) + if len_value > len_self: + return False + + for i in range(0, len_self - len_value + 1): + sub_list = self[i:i+len_value] + if sub_list == value: + return True + return False + + def __repr__(self): + return pprint.pformat(list(self)) + + +def _check_and_set_parent(parent, value, name, new_name): + if not _is_instance_mock(value): + return False + if ((value._mock_name or value._mock_new_name) or + (value._mock_parent is not None) or + (value._mock_new_parent is not None)): + return False + + _parent = parent + while _parent is not None: + # setting a mock (value) as a child or return value of itself + # should not modify the mock + if _parent is value: + return False + _parent = _parent._mock_new_parent + + if new_name: + value._mock_new_parent = parent + value._mock_new_name = new_name + if name: + value._mock_parent = parent + value._mock_name = name + return True + + + +class Base(object): + _mock_return_value = DEFAULT + _mock_side_effect = None + def __init__(self, *args, **kwargs): + pass + + + +class NonCallableMock(Base): + """A non-callable version of `Mock`""" + + def __new__(cls, *args, **kw): + # every instance has its own class + # so we can create magic methods on the + # class without stomping on other mocks + new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__}) + instance = object.__new__(new) + return instance + + + def __init__( + self, spec=None, wraps=None, name=None, spec_set=None, + parent=None, _spec_state=None, _new_name='', _new_parent=None, + **kwargs + ): + if _new_parent is None: + _new_parent = parent + + __dict__ = self.__dict__ + __dict__['_mock_parent'] = parent + __dict__['_mock_name'] = name + __dict__['_mock_new_name'] = _new_name + __dict__['_mock_new_parent'] = _new_parent + + if spec_set is not None: + spec = spec_set + spec_set = True + + self._mock_add_spec(spec, spec_set) + + __dict__['_mock_children'] = {} + __dict__['_mock_wraps'] = wraps + __dict__['_mock_delegate'] = None + + __dict__['_mock_called'] = False + __dict__['_mock_call_args'] = None + __dict__['_mock_call_count'] = 0 + __dict__['_mock_call_args_list'] = _CallList() + __dict__['_mock_mock_calls'] = _CallList() + + __dict__['method_calls'] = _CallList() + + if kwargs: + self.configure_mock(**kwargs) + + _super(NonCallableMock, self).__init__( + spec, wraps, name, spec_set, parent, + _spec_state + ) + + + def attach_mock(self, mock, attribute): + """ + Attach a mock as an attribute of this one, replacing its name and + parent. Calls to the attached mock will be recorded in the + `method_calls` and `mock_calls` attributes of this one.""" + mock._mock_parent = None + mock._mock_new_parent = None + mock._mock_name = '' + mock._mock_new_name = None + + setattr(self, attribute, mock) + + + def mock_add_spec(self, spec, spec_set=False): + """Add a spec to a mock. `spec` can either be an object or a + list of strings. Only attributes on the `spec` can be fetched as + attributes from the mock. + + If `spec_set` is True then only attributes on the spec can be set.""" + self._mock_add_spec(spec, spec_set) + + + def _mock_add_spec(self, spec, spec_set): + _spec_class = None + + if spec is not None and not _is_list(spec): + if isinstance(spec, ClassTypes): + _spec_class = spec + else: + _spec_class = _get_class(spec) + + spec = dir(spec) + + __dict__ = self.__dict__ + __dict__['_spec_class'] = _spec_class + __dict__['_spec_set'] = spec_set + __dict__['_mock_methods'] = spec + + + def __get_return_value(self): + ret = self._mock_return_value + if self._mock_delegate is not None: + ret = self._mock_delegate.return_value + + if ret is DEFAULT: + ret = self._get_child_mock( + _new_parent=self, _new_name='()' + ) + self.return_value = ret + return ret + + + def __set_return_value(self, value): + if self._mock_delegate is not None: + self._mock_delegate.return_value = value + else: + self._mock_return_value = value + _check_and_set_parent(self, value, None, '()') + + __return_value_doc = "The value to be returned when the mock is called." + return_value = property(__get_return_value, __set_return_value, + __return_value_doc) + + + @property + def __class__(self): + if self._spec_class is None: + return type(self) + return self._spec_class + + called = _delegating_property('called') + call_count = _delegating_property('call_count') + call_args = _delegating_property('call_args') + call_args_list = _delegating_property('call_args_list') + mock_calls = _delegating_property('mock_calls') + + + def __get_side_effect(self): + sig = self._mock_delegate + if sig is None: + return self._mock_side_effect + return sig.side_effect + + def __set_side_effect(self, value): + value = _try_iter(value) + sig = self._mock_delegate + if sig is None: + self._mock_side_effect = value + else: + sig.side_effect = value + + side_effect = property(__get_side_effect, __set_side_effect) + + + def reset_mock(self): + "Restore the mock object to its initial state." + self.called = False + self.call_args = None + self.call_count = 0 + self.mock_calls = _CallList() + self.call_args_list = _CallList() + self.method_calls = _CallList() + + for child in self._mock_children.values(): + if isinstance(child, _SpecState): + continue + child.reset_mock() + + ret = self._mock_return_value + if _is_instance_mock(ret) and ret is not self: + ret.reset_mock() + + + def configure_mock(self, **kwargs): + """Set attributes on the mock through keyword arguments. + + Attributes plus return values and side effects can be set on child + mocks using standard dot notation and unpacking a dictionary in the + method call: + + >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} + >>> mock.configure_mock(**attrs)""" + for arg, val in sorted(kwargs.items(), + # we sort on the number of dots so that + # attributes are set before we set attributes on + # attributes + key=lambda entry: entry[0].count('.')): + args = arg.split('.') + final = args.pop() + obj = self + for entry in args: + obj = getattr(obj, entry) + setattr(obj, final, val) + + + def __getattr__(self, name): + if name == '_mock_methods': + raise AttributeError(name) + elif self._mock_methods is not None: + if name not in self._mock_methods or name in _all_magics: + raise AttributeError("Mock object has no attribute %r" % name) + elif _is_magic(name): + raise AttributeError(name) + + result = self._mock_children.get(name) + if result is _deleted: + raise AttributeError(name) + elif result is None: + wraps = None + if self._mock_wraps is not None: + # XXXX should we get the attribute without triggering code + # execution? + wraps = getattr(self._mock_wraps, name) + + result = self._get_child_mock( + parent=self, name=name, wraps=wraps, _new_name=name, + _new_parent=self + ) + self._mock_children[name] = result + + elif isinstance(result, _SpecState): + result = create_autospec( + result.spec, result.spec_set, result.instance, + result.parent, result.name + ) + self._mock_children[name] = result + + return result + + + def __repr__(self): + _name_list = [self._mock_new_name] + _parent = self._mock_new_parent + last = self + + dot = '.' + if _name_list == ['()']: + dot = '' + seen = set() + while _parent is not None: + last = _parent + + _name_list.append(_parent._mock_new_name + dot) + dot = '.' + if _parent._mock_new_name == '()': + dot = '' + + _parent = _parent._mock_new_parent + + # use ids here so as not to call __hash__ on the mocks + if id(_parent) in seen: + break + seen.add(id(_parent)) + + _name_list = list(reversed(_name_list)) + _first = last._mock_name or 'mock' + if len(_name_list) > 1: + if _name_list[1] not in ('()', '().'): + _first += '.' + _name_list[0] = _first + name = ''.join(_name_list) + + name_string = '' + if name not in ('mock', 'mock.'): + name_string = ' name=%r' % name + + spec_string = '' + if self._spec_class is not None: + spec_string = ' spec=%r' + if self._spec_set: + spec_string = ' spec_set=%r' + spec_string = spec_string % self._spec_class.__name__ + return "<%s%s%s id='%s'>" % ( + type(self).__name__, + name_string, + spec_string, + id(self) + ) + + + def __dir__(self): + """Filter the output of `dir(mock)` to only useful members. + XXXX + """ + extras = self._mock_methods or [] + from_type = dir(type(self)) + from_dict = list(self.__dict__) + + if FILTER_DIR: + from_type = [e for e in from_type if not e.startswith('_')] + from_dict = [e for e in from_dict if not e.startswith('_') or + _is_magic(e)] + return sorted(set(extras + from_type + from_dict + + list(self._mock_children))) + + + def __setattr__(self, name, value): + if name in _allowed_names: + # property setters go through here + return object.__setattr__(self, name, value) + elif (self._spec_set and self._mock_methods is not None and + name not in self._mock_methods and + name not in self.__dict__): + raise AttributeError("Mock object has no attribute '%s'" % name) + elif name in _unsupported_magics: + msg = 'Attempting to set unsupported magic method %r.' % name + raise AttributeError(msg) + elif name in _all_magics: + if self._mock_methods is not None and name not in self._mock_methods: + raise AttributeError("Mock object has no attribute '%s'" % name) + + if not _is_instance_mock(value): + setattr(type(self), name, _get_method(name, value)) + original = value + value = lambda *args, **kw: original(self, *args, **kw) + else: + # only set _new_name and not name so that mock_calls is tracked + # but not method calls + _check_and_set_parent(self, value, None, name) + setattr(type(self), name, value) + self._mock_children[name] = value + elif name == '__class__': + self._spec_class = value + return + else: + if _check_and_set_parent(self, value, name, name): + self._mock_children[name] = value + return object.__setattr__(self, name, value) + + + def __delattr__(self, name): + if name in _all_magics and name in type(self).__dict__: + delattr(type(self), name) + if name not in self.__dict__: + # for magic methods that are still MagicProxy objects and + # not set on the instance itself + return + + if name in self.__dict__: + object.__delattr__(self, name) + + obj = self._mock_children.get(name, _missing) + if obj is _deleted: + raise AttributeError(name) + if obj is not _missing: + del self._mock_children[name] + self._mock_children[name] = _deleted + + + + def _format_mock_call_signature(self, args, kwargs): + name = self._mock_name or 'mock' + return _format_call_signature(name, args, kwargs) + + + def _format_mock_failure_message(self, args, kwargs): + message = 'Expected call: %s\nActual call: %s' + expected_string = self._format_mock_call_signature(args, kwargs) + call_args = self.call_args + if len(call_args) == 3: + call_args = call_args[1:] + actual_string = self._format_mock_call_signature(*call_args) + return message % (expected_string, actual_string) + + + def assert_called_with(_mock_self, *args, **kwargs): + """assert that the mock was called with the specified arguments. + + Raises an AssertionError if the args and keyword args passed in are + different to the last call to the mock.""" + self = _mock_self + if self.call_args is None: + expected = self._format_mock_call_signature(args, kwargs) + raise AssertionError('Expected call: %s\nNot called' % (expected,)) + + if self.call_args != (args, kwargs): + msg = self._format_mock_failure_message(args, kwargs) + raise AssertionError(msg) + + + def assert_called_once_with(_mock_self, *args, **kwargs): + """assert that the mock was called exactly once and with the specified + arguments.""" + self = _mock_self + if not self.call_count == 1: + msg = ("Expected to be called once. Called %s times." % + self.call_count) + raise AssertionError(msg) + return self.assert_called_with(*args, **kwargs) + + + def assert_has_calls(self, calls, any_order=False): + """assert the mock has been called with the specified calls. + The `mock_calls` list is checked for the calls. + + If `any_order` is False (the default) then the calls must be + sequential. There can be extra calls before or after the + specified calls. + + If `any_order` is True then the calls can be in any order, but + they must all appear in `mock_calls`.""" + if not any_order: + if calls not in self.mock_calls: + raise AssertionError( + 'Calls not found.\nExpected: %r\n' + 'Actual: %r' % (calls, self.mock_calls) + ) + return + + all_calls = list(self.mock_calls) + + not_found = [] + for kall in calls: + try: + all_calls.remove(kall) + except ValueError: + not_found.append(kall) + if not_found: + raise AssertionError( + '%r not all found in call list' % (tuple(not_found),) + ) + + + def assert_any_call(self, *args, **kwargs): + """assert the mock has been called with the specified arguments. + + The assert passes if the mock has *ever* been called, unlike + `assert_called_with` and `assert_called_once_with` that only pass if + the call is the most recent one.""" + kall = call(*args, **kwargs) + if kall not in self.call_args_list: + expected_string = self._format_mock_call_signature(args, kwargs) + raise AssertionError( + '%s call not found' % expected_string + ) + + + def _get_child_mock(self, **kw): + """Create the child mocks for attributes and return value. + By default child mocks will be the same type as the parent. + Subclasses of Mock may want to override this to customize the way + child mocks are made. + + For non-callable mocks the callable variant will be used (rather than + any custom subclass).""" + _type = type(self) + if not issubclass(_type, CallableMixin): + if issubclass(_type, NonCallableMagicMock): + klass = MagicMock + elif issubclass(_type, NonCallableMock) : + klass = Mock + else: + klass = _type.__mro__[1] + return klass(**kw) + + + +def _try_iter(obj): + if obj is None: + return obj + if _is_exception(obj): + return obj + if _callable(obj): + return obj + try: + return iter(obj) + except TypeError: + # XXXX backwards compatibility + # but this will blow up on first call - so maybe we should fail early? + return obj + + + +class CallableMixin(Base): + + def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, + wraps=None, name=None, spec_set=None, parent=None, + _spec_state=None, _new_name='', _new_parent=None, **kwargs): + self.__dict__['_mock_return_value'] = return_value + + _super(CallableMixin, self).__init__( + spec, wraps, name, spec_set, parent, + _spec_state, _new_name, _new_parent, **kwargs + ) + + self.side_effect = side_effect + + + def _mock_check_sig(self, *args, **kwargs): + # stub method that can be replaced with one with a specific signature + pass + + + def __call__(_mock_self, *args, **kwargs): + # can't use self in-case a function / method we are mocking uses self + # in the signature + _mock_self._mock_check_sig(*args, **kwargs) + return _mock_self._mock_call(*args, **kwargs) + + + def _mock_call(_mock_self, *args, **kwargs): + self = _mock_self + self.called = True + self.call_count += 1 + self.call_args = _Call((args, kwargs), two=True) + self.call_args_list.append(_Call((args, kwargs), two=True)) + + _new_name = self._mock_new_name + _new_parent = self._mock_new_parent + self.mock_calls.append(_Call(('', args, kwargs))) + + seen = set() + skip_next_dot = _new_name == '()' + do_method_calls = self._mock_parent is not None + name = self._mock_name + while _new_parent is not None: + this_mock_call = _Call((_new_name, args, kwargs)) + if _new_parent._mock_new_name: + dot = '.' + if skip_next_dot: + dot = '' + + skip_next_dot = False + if _new_parent._mock_new_name == '()': + skip_next_dot = True + + _new_name = _new_parent._mock_new_name + dot + _new_name + + if do_method_calls: + if _new_name == name: + this_method_call = this_mock_call + else: + this_method_call = _Call((name, args, kwargs)) + _new_parent.method_calls.append(this_method_call) + + do_method_calls = _new_parent._mock_parent is not None + if do_method_calls: + name = _new_parent._mock_name + '.' + name + + _new_parent.mock_calls.append(this_mock_call) + _new_parent = _new_parent._mock_new_parent + + # use ids here so as not to call __hash__ on the mocks + _new_parent_id = id(_new_parent) + if _new_parent_id in seen: + break + seen.add(_new_parent_id) + + ret_val = DEFAULT + effect = self.side_effect + if effect is not None: + if _is_exception(effect): + raise effect + + if not _callable(effect): + result = next(effect) + if _is_exception(result): + raise result + return result + + ret_val = effect(*args, **kwargs) + if ret_val is DEFAULT: + ret_val = self.return_value + + if (self._mock_wraps is not None and + self._mock_return_value is DEFAULT): + return self._mock_wraps(*args, **kwargs) + if ret_val is DEFAULT: + ret_val = self.return_value + return ret_val + + + +class Mock(CallableMixin, NonCallableMock): + """ + Create a new `Mock` object. `Mock` takes several optional arguments + that specify the behaviour of the Mock object: + + * `spec`: This can be either a list of strings or an existing object (a + class or instance) that acts as the specification for the mock object. If + you pass in an object then a list of strings is formed by calling dir on + the object (excluding unsupported magic attributes and methods). Accessing + any attribute not in this list will raise an `AttributeError`. + + If `spec` is an object (rather than a list of strings) then + `mock.__class__` returns the class of the spec object. This allows mocks + to pass `isinstance` tests. + + * `spec_set`: A stricter variant of `spec`. If used, attempting to *set* + or get an attribute on the mock that isn't on the object passed as + `spec_set` will raise an `AttributeError`. + + * `side_effect`: A function to be called whenever the Mock is called. See + the `side_effect` attribute. Useful for raising exceptions or + dynamically changing return values. The function is called with the same + arguments as the mock, and unless it returns `DEFAULT`, the return + value of this function is used as the return value. + + Alternatively `side_effect` can be an exception class or instance. In + this case the exception will be raised when the mock is called. + + If `side_effect` is an iterable then each call to the mock will return + the next value from the iterable. If any of the members of the iterable + are exceptions they will be raised instead of returned. + + * `return_value`: The value returned when the mock is called. By default + this is a new Mock (created on first access). See the + `return_value` attribute. + + * `wraps`: Item for the mock object to wrap. If `wraps` is not None then + calling the Mock will pass the call through to the wrapped object + (returning the real result). Attribute access on the mock will return a + Mock object that wraps the corresponding attribute of the wrapped object + (so attempting to access an attribute that doesn't exist will raise an + `AttributeError`). + + If the mock has an explicit `return_value` set then calls are not passed + to the wrapped object and the `return_value` is returned instead. + + * `name`: If the mock has a name then it will be used in the repr of the + mock. This can be useful for debugging. The name is propagated to child + mocks. + + Mocks can also be called with arbitrary keyword arguments. These will be + used to set attributes on the mock after it is created. + """ + + + +def _dot_lookup(thing, comp, import_path): + try: + return getattr(thing, comp) + except AttributeError: + __import__(import_path) + return getattr(thing, comp) + + +def _importer(target): + components = target.split('.') + import_path = components.pop(0) + thing = __import__(import_path) + + for comp in components: + import_path += ".%s" % comp + thing = _dot_lookup(thing, comp, import_path) + return thing + + +def _is_started(patcher): + # XXXX horrible + return hasattr(patcher, 'is_local') + + +class _patch(object): + + attribute_name = None + _active_patches = set() + + def __init__( + self, getter, attribute, new, spec, create, + spec_set, autospec, new_callable, kwargs + ): + if new_callable is not None: + if new is not DEFAULT: + raise ValueError( + "Cannot use 'new' and 'new_callable' together" + ) + if autospec is not None: + raise ValueError( + "Cannot use 'autospec' and 'new_callable' together" + ) + + self.getter = getter + self.attribute = attribute + self.new = new + self.new_callable = new_callable + self.spec = spec + self.create = create + self.has_local = False + self.spec_set = spec_set + self.autospec = autospec + self.kwargs = kwargs + self.additional_patchers = [] + + + def copy(self): + patcher = _patch( + self.getter, self.attribute, self.new, self.spec, + self.create, self.spec_set, + self.autospec, self.new_callable, self.kwargs + ) + patcher.attribute_name = self.attribute_name + patcher.additional_patchers = [ + p.copy() for p in self.additional_patchers + ] + return patcher + + + def __call__(self, func): + if isinstance(func, ClassTypes): + return self.decorate_class(func) + return self.decorate_callable(func) + + + def decorate_class(self, klass): + for attr in dir(klass): + if not attr.startswith(patch.TEST_PREFIX): + continue + + attr_value = getattr(klass, attr) + if not hasattr(attr_value, "__call__"): + continue + + patcher = self.copy() + setattr(klass, attr, patcher(attr_value)) + return klass + + + def decorate_callable(self, func): + if hasattr(func, 'patchings'): + func.patchings.append(self) + return func + + @wraps(func) + def patched(*args, **keywargs): + # don't use a with here (backwards compatability with Python 2.4) + extra_args = [] + entered_patchers = [] + + # can't use try...except...finally because of Python 2.4 + # compatibility + exc_info = tuple() + try: + try: + for patching in patched.patchings: + arg = patching.__enter__() + entered_patchers.append(patching) + if patching.attribute_name is not None: + keywargs.update(arg) + elif patching.new is DEFAULT: + extra_args.append(arg) + + args += tuple(extra_args) + return func(*args, **keywargs) + except: + if (patching not in entered_patchers and + _is_started(patching)): + # the patcher may have been started, but an exception + # raised whilst entering one of its additional_patchers + entered_patchers.append(patching) + # Pass the exception to __exit__ + exc_info = sys.exc_info() + # re-raise the exception + raise + finally: + for patching in reversed(entered_patchers): + patching.__exit__(*exc_info) + + patched.patchings = [self] + if hasattr(func, 'func_code'): + # not in Python 3 + patched.compat_co_firstlineno = getattr( + func, "compat_co_firstlineno", + func.func_code.co_firstlineno + ) + return patched + + + def get_original(self): + target = self.getter() + name = self.attribute + + original = DEFAULT + local = False + + try: + original = target.__dict__[name] + except (AttributeError, KeyError): + original = getattr(target, name, DEFAULT) + else: + local = True + + if not self.create and original is DEFAULT: + raise AttributeError( + "%s does not have the attribute %r" % (target, name) + ) + return original, local + + + def __enter__(self): + """Perform the patch.""" + new, spec, spec_set = self.new, self.spec, self.spec_set + autospec, kwargs = self.autospec, self.kwargs + new_callable = self.new_callable + self.target = self.getter() + + # normalise False to None + if spec is False: + spec = None + if spec_set is False: + spec_set = None + if autospec is False: + autospec = None + + if spec is not None and autospec is not None: + raise TypeError("Can't specify spec and autospec") + if ((spec is not None or autospec is not None) and + spec_set not in (True, None)): + raise TypeError("Can't provide explicit spec_set *and* spec or autospec") + + original, local = self.get_original() + + if new is DEFAULT and autospec is None: + inherit = False + if spec is True: + # set spec to the object we are replacing + spec = original + if spec_set is True: + spec_set = original + spec = None + elif spec is not None: + if spec_set is True: + spec_set = spec + spec = None + elif spec_set is True: + spec_set = original + + if spec is not None or spec_set is not None: + if original is DEFAULT: + raise TypeError("Can't use 'spec' with create=True") + if isinstance(original, ClassTypes): + # If we're patching out a class and there is a spec + inherit = True + + Klass = MagicMock + _kwargs = {} + if new_callable is not None: + Klass = new_callable + elif spec is not None or spec_set is not None: + this_spec = spec + if spec_set is not None: + this_spec = spec_set + if _is_list(this_spec): + not_callable = '__call__' not in this_spec + else: + not_callable = not _callable(this_spec) + if not_callable: + Klass = NonCallableMagicMock + + if spec is not None: + _kwargs['spec'] = spec + if spec_set is not None: + _kwargs['spec_set'] = spec_set + + # add a name to mocks + if (isinstance(Klass, type) and + issubclass(Klass, NonCallableMock) and self.attribute): + _kwargs['name'] = self.attribute + + _kwargs.update(kwargs) + new = Klass(**_kwargs) + + if inherit and _is_instance_mock(new): + # we can only tell if the instance should be callable if the + # spec is not a list + this_spec = spec + if spec_set is not None: + this_spec = spec_set + if (not _is_list(this_spec) and not + _instance_callable(this_spec)): + Klass = NonCallableMagicMock + + _kwargs.pop('name') + new.return_value = Klass(_new_parent=new, _new_name='()', + **_kwargs) + elif autospec is not None: + # spec is ignored, new *must* be default, spec_set is treated + # as a boolean. Should we check spec is not None and that spec_set + # is a bool? + if new is not DEFAULT: + raise TypeError( + "autospec creates the mock for you. Can't specify " + "autospec and new." + ) + if original is DEFAULT: + raise TypeError("Can't use 'autospec' with create=True") + spec_set = bool(spec_set) + if autospec is True: + autospec = original + + new = create_autospec(autospec, spec_set=spec_set, + _name=self.attribute, **kwargs) + elif kwargs: + # can't set keyword args when we aren't creating the mock + # XXXX If new is a Mock we could call new.configure_mock(**kwargs) + raise TypeError("Can't pass kwargs to a mock we aren't creating") + + new_attr = new + + self.temp_original = original + self.is_local = local + setattr(self.target, self.attribute, new_attr) + if self.attribute_name is not None: + extra_args = {} + if self.new is DEFAULT: + extra_args[self.attribute_name] = new + for patching in self.additional_patchers: + arg = patching.__enter__() + if patching.new is DEFAULT: + extra_args.update(arg) + return extra_args + + return new + + + def __exit__(self, *exc_info): + """Undo the patch.""" + if not _is_started(self): + raise RuntimeError('stop called on unstarted patcher') + + if self.is_local and self.temp_original is not DEFAULT: + setattr(self.target, self.attribute, self.temp_original) + else: + delattr(self.target, self.attribute) + if not self.create and not hasattr(self.target, self.attribute): + # needed for proxy objects like django settings + setattr(self.target, self.attribute, self.temp_original) + + del self.temp_original + del self.is_local + del self.target + for patcher in reversed(self.additional_patchers): + if _is_started(patcher): + patcher.__exit__(*exc_info) + + + def start(self): + """Activate a patch, returning any created mock.""" + result = self.__enter__() + self._active_patches.add(self) + return result + + + def stop(self): + """Stop an active patch.""" + self._active_patches.discard(self) + return self.__exit__() + + + +def _get_target(target): + try: + target, attribute = target.rsplit('.', 1) + except (TypeError, ValueError): + raise TypeError("Need a valid target to patch. You supplied: %r" % + (target,)) + getter = lambda: _importer(target) + return getter, attribute + + +def _patch_object( + target, attribute, new=DEFAULT, spec=None, + create=False, spec_set=None, autospec=None, + new_callable=None, **kwargs + ): + """ + patch.object(target, attribute, new=DEFAULT, spec=None, create=False, + spec_set=None, autospec=None, new_callable=None, **kwargs) + + patch the named member (`attribute`) on an object (`target`) with a mock + object. + + `patch.object` can be used as a decorator, class decorator or a context + manager. Arguments `new`, `spec`, `create`, `spec_set`, + `autospec` and `new_callable` have the same meaning as for `patch`. Like + `patch`, `patch.object` takes arbitrary keyword arguments for configuring + the mock object it creates. + + When used as a class decorator `patch.object` honours `patch.TEST_PREFIX` + for choosing which methods to wrap. + """ + getter = lambda: target + return _patch( + getter, attribute, new, spec, create, + spec_set, autospec, new_callable, kwargs + ) + + +def _patch_multiple(target, spec=None, create=False, spec_set=None, + autospec=None, new_callable=None, **kwargs): + """Perform multiple patches in a single call. It takes the object to be + patched (either as an object or a string to fetch the object by importing) + and keyword arguments for the patches:: + + with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): + ... + + Use `DEFAULT` as the value if you want `patch.multiple` to create + mocks for you. In this case the created mocks are passed into a decorated + function by keyword, and a dictionary is returned when `patch.multiple` is + used as a context manager. + + `patch.multiple` can be used as a decorator, class decorator or a context + manager. The arguments `spec`, `spec_set`, `create`, + `autospec` and `new_callable` have the same meaning as for `patch`. These + arguments will be applied to *all* patches done by `patch.multiple`. + + When used as a class decorator `patch.multiple` honours `patch.TEST_PREFIX` + for choosing which methods to wrap. + """ + if type(target) in (unicode, str): + getter = lambda: _importer(target) + else: + getter = lambda: target + + if not kwargs: + raise ValueError( + 'Must supply at least one keyword argument with patch.multiple' + ) + # need to wrap in a list for python 3, where items is a view + items = list(kwargs.items()) + attribute, new = items[0] + patcher = _patch( + getter, attribute, new, spec, create, spec_set, + autospec, new_callable, {} + ) + patcher.attribute_name = attribute + for attribute, new in items[1:]: + this_patcher = _patch( + getter, attribute, new, spec, create, spec_set, + autospec, new_callable, {} + ) + this_patcher.attribute_name = attribute + patcher.additional_patchers.append(this_patcher) + return patcher + + +def patch( + target, new=DEFAULT, spec=None, create=False, + spec_set=None, autospec=None, new_callable=None, **kwargs + ): + """ + `patch` acts as a function decorator, class decorator or a context + manager. Inside the body of the function or with statement, the `target` + is patched with a `new` object. When the function/with statement exits + the patch is undone. + + If `new` is omitted, then the target is replaced with a + `MagicMock`. If `patch` is used as a decorator and `new` is + omitted, the created mock is passed in as an extra argument to the + decorated function. If `patch` is used as a context manager the created + mock is returned by the context manager. + + `target` should be a string in the form `'package.module.ClassName'`. The + `target` is imported and the specified object replaced with the `new` + object, so the `target` must be importable from the environment you are + calling `patch` from. The target is imported when the decorated function + is executed, not at decoration time. + + The `spec` and `spec_set` keyword arguments are passed to the `MagicMock` + if patch is creating one for you. + + In addition you can pass `spec=True` or `spec_set=True`, which causes + patch to pass in the object being mocked as the spec/spec_set object. + + `new_callable` allows you to specify a different class, or callable object, + that will be called to create the `new` object. By default `MagicMock` is + used. + + A more powerful form of `spec` is `autospec`. If you set `autospec=True` + then the mock with be created with a spec from the object being replaced. + All attributes of the mock will also have the spec of the corresponding + attribute of the object being replaced. Methods and functions being + mocked will have their arguments checked and will raise a `TypeError` if + they are called with the wrong signature. For mocks replacing a class, + their return value (the 'instance') will have the same spec as the class. + + Instead of `autospec=True` you can pass `autospec=some_object` to use an + arbitrary object as the spec instead of the one being replaced. + + By default `patch` will fail to replace attributes that don't exist. If + you pass in `create=True`, and the attribute doesn't exist, patch will + create the attribute for you when the patched function is called, and + delete it again afterwards. This is useful for writing tests against + attributes that your production code creates at runtime. It is off by by + default because it can be dangerous. With it switched on you can write + passing tests against APIs that don't actually exist! + + Patch can be used as a `TestCase` class decorator. It works by + decorating each test method in the class. This reduces the boilerplate + code when your test methods share a common patchings set. `patch` finds + tests by looking for method names that start with `patch.TEST_PREFIX`. + By default this is `test`, which matches the way `unittest` finds tests. + You can specify an alternative prefix by setting `patch.TEST_PREFIX`. + + Patch can be used as a context manager, with the with statement. Here the + patching applies to the indented block after the with statement. If you + use "as" then the patched object will be bound to the name after the + "as"; very useful if `patch` is creating a mock object for you. + + `patch` takes arbitrary keyword arguments. These will be passed to + the `Mock` (or `new_callable`) on construction. + + `patch.dict(...)`, `patch.multiple(...)` and `patch.object(...)` are + available for alternate use-cases. + """ + getter, attribute = _get_target(target) + return _patch( + getter, attribute, new, spec, create, + spec_set, autospec, new_callable, kwargs + ) + + +class _patch_dict(object): + """ + Patch a dictionary, or dictionary like object, and restore the dictionary + to its original state after the test. + + `in_dict` can be a dictionary or a mapping like container. If it is a + mapping then it must at least support getting, setting and deleting items + plus iterating over keys. + + `in_dict` can also be a string specifying the name of the dictionary, which + will then be fetched by importing it. + + `values` can be a dictionary of values to set in the dictionary. `values` + can also be an iterable of `(key, value)` pairs. + + If `clear` is True then the dictionary will be cleared before the new + values are set. + + `patch.dict` can also be called with arbitrary keyword arguments to set + values in the dictionary:: + + with patch.dict('sys.modules', mymodule=Mock(), other_module=Mock()): + ... + + `patch.dict` can be used as a context manager, decorator or class + decorator. When used as a class decorator `patch.dict` honours + `patch.TEST_PREFIX` for choosing which methods to wrap. + """ + + def __init__(self, in_dict, values=(), clear=False, **kwargs): + if isinstance(in_dict, basestring): + in_dict = _importer(in_dict) + self.in_dict = in_dict + # support any argument supported by dict(...) constructor + self.values = dict(values) + self.values.update(kwargs) + self.clear = clear + self._original = None + + + def __call__(self, f): + if isinstance(f, ClassTypes): + return self.decorate_class(f) + @wraps(f) + def _inner(*args, **kw): + self._patch_dict() + try: + return f(*args, **kw) + finally: + self._unpatch_dict() + + return _inner + + + def decorate_class(self, klass): + for attr in dir(klass): + attr_value = getattr(klass, attr) + if (attr.startswith(patch.TEST_PREFIX) and + hasattr(attr_value, "__call__")): + decorator = _patch_dict(self.in_dict, self.values, self.clear) + decorated = decorator(attr_value) + setattr(klass, attr, decorated) + return klass + + + def __enter__(self): + """Patch the dict.""" + self._patch_dict() + + + def _patch_dict(self): + values = self.values + in_dict = self.in_dict + clear = self.clear + + try: + original = in_dict.copy() + except AttributeError: + # dict like object with no copy method + # must support iteration over keys + original = {} + for key in in_dict: + original[key] = in_dict[key] + self._original = original + + if clear: + _clear_dict(in_dict) + + try: + in_dict.update(values) + except AttributeError: + # dict like object with no update method + for key in values: + in_dict[key] = values[key] + + + def _unpatch_dict(self): + in_dict = self.in_dict + original = self._original + + _clear_dict(in_dict) + + try: + in_dict.update(original) + except AttributeError: + for key in original: + in_dict[key] = original[key] + + + def __exit__(self, *args): + """Unpatch the dict.""" + self._unpatch_dict() + return False + + start = __enter__ + stop = __exit__ + + +def _clear_dict(in_dict): + try: + in_dict.clear() + except AttributeError: + keys = list(in_dict) + for key in keys: + del in_dict[key] + + +def _patch_stopall(): + """Stop all active patches.""" + for patch in list(_patch._active_patches): + patch.stop() + + +patch.object = _patch_object +patch.dict = _patch_dict +patch.multiple = _patch_multiple +patch.stopall = _patch_stopall +patch.TEST_PREFIX = 'test' + +magic_methods = ( + "lt le gt ge eq ne " + "getitem setitem delitem " + "len contains iter " + "hash str sizeof " + "enter exit " + "divmod neg pos abs invert " + "complex int float index " + "trunc floor ceil " +) + +numerics = "add sub mul div floordiv mod lshift rshift and xor or pow " +inplace = ' '.join('i%s' % n for n in numerics.split()) +right = ' '.join('r%s' % n for n in numerics.split()) +extra = '' +if inPy3k: + extra = 'bool next ' +else: + extra = 'unicode long nonzero oct hex truediv rtruediv ' + +# not including __prepare__, __instancecheck__, __subclasscheck__ +# (as they are metaclass methods) +# __del__ is not supported at all as it causes problems if it exists + +_non_defaults = set('__%s__' % method for method in [ + 'cmp', 'getslice', 'setslice', 'coerce', 'subclasses', + 'format', 'get', 'set', 'delete', 'reversed', + 'missing', 'reduce', 'reduce_ex', 'getinitargs', + 'getnewargs', 'getstate', 'setstate', 'getformat', + 'setformat', 'repr', 'dir' +]) + + +def _get_method(name, func): + "Turns a callable object (like a mock) into a real function" + def method(self, *args, **kw): + return func(self, *args, **kw) + method.__name__ = name + return method + + +_magics = set( + '__%s__' % method for method in + ' '.join([magic_methods, numerics, inplace, right, extra]).split() +) + +_all_magics = _magics | _non_defaults + +_unsupported_magics = set([ + '__getattr__', '__setattr__', + '__init__', '__new__', '__prepare__' + '__instancecheck__', '__subclasscheck__', + '__del__' +]) + +_calculate_return_value = { + '__hash__': lambda self: object.__hash__(self), + '__str__': lambda self: object.__str__(self), + '__sizeof__': lambda self: object.__sizeof__(self), + '__unicode__': lambda self: unicode(object.__str__(self)), +} + +_return_values = { + '__lt__': NotImplemented, + '__gt__': NotImplemented, + '__le__': NotImplemented, + '__ge__': NotImplemented, + '__int__': 1, + '__contains__': False, + '__len__': 0, + '__exit__': False, + '__complex__': 1j, + '__float__': 1.0, + '__bool__': True, + '__nonzero__': True, + '__oct__': '1', + '__hex__': '0x1', + '__long__': long(1), + '__index__': 1, +} + + +def _get_eq(self): + def __eq__(other): + ret_val = self.__eq__._mock_return_value + if ret_val is not DEFAULT: + return ret_val + return self is other + return __eq__ + +def _get_ne(self): + def __ne__(other): + if self.__ne__._mock_return_value is not DEFAULT: + return DEFAULT + return self is not other + return __ne__ + +def _get_iter(self): + def __iter__(): + ret_val = self.__iter__._mock_return_value + if ret_val is DEFAULT: + return iter([]) + # if ret_val was already an iterator, then calling iter on it should + # return the iterator unchanged + return iter(ret_val) + return __iter__ + +_side_effect_methods = { + '__eq__': _get_eq, + '__ne__': _get_ne, + '__iter__': _get_iter, +} + + + +def _set_return_value(mock, method, name): + fixed = _return_values.get(name, DEFAULT) + if fixed is not DEFAULT: + method.return_value = fixed + return + + return_calulator = _calculate_return_value.get(name) + if return_calulator is not None: + try: + return_value = return_calulator(mock) + except AttributeError: + # XXXX why do we return AttributeError here? + # set it as a side_effect instead? + return_value = AttributeError(name) + method.return_value = return_value + return + + side_effector = _side_effect_methods.get(name) + if side_effector is not None: + method.side_effect = side_effector(mock) + + + +class MagicMixin(object): + def __init__(self, *args, **kw): + _super(MagicMixin, self).__init__(*args, **kw) + self._mock_set_magics() + + + def _mock_set_magics(self): + these_magics = _magics + + if self._mock_methods is not None: + these_magics = _magics.intersection(self._mock_methods) + + remove_magics = set() + remove_magics = _magics - these_magics + + for entry in remove_magics: + if entry in type(self).__dict__: + # remove unneeded magic methods + delattr(self, entry) + + # don't overwrite existing attributes if called a second time + these_magics = these_magics - set(type(self).__dict__) + + _type = type(self) + for entry in these_magics: + setattr(_type, entry, MagicProxy(entry, self)) + + + +class NonCallableMagicMock(MagicMixin, NonCallableMock): + """A version of `MagicMock` that isn't callable.""" + def mock_add_spec(self, spec, spec_set=False): + """Add a spec to a mock. `spec` can either be an object or a + list of strings. Only attributes on the `spec` can be fetched as + attributes from the mock. + + If `spec_set` is True then only attributes on the spec can be set.""" + self._mock_add_spec(spec, spec_set) + self._mock_set_magics() + + + +class MagicMock(MagicMixin, Mock): + """ + MagicMock is a subclass of Mock with default implementations + of most of the magic methods. You can use MagicMock without having to + configure the magic methods yourself. + + If you use the `spec` or `spec_set` arguments then *only* magic + methods that exist in the spec will be created. + + Attributes and the return value of a `MagicMock` will also be `MagicMocks`. + """ + def mock_add_spec(self, spec, spec_set=False): + """Add a spec to a mock. `spec` can either be an object or a + list of strings. Only attributes on the `spec` can be fetched as + attributes from the mock. + + If `spec_set` is True then only attributes on the spec can be set.""" + self._mock_add_spec(spec, spec_set) + self._mock_set_magics() + + + +class MagicProxy(object): + def __init__(self, name, parent): + self.name = name + self.parent = parent + + def __call__(self, *args, **kwargs): + m = self.create_mock() + return m(*args, **kwargs) + + def create_mock(self): + entry = self.name + parent = self.parent + m = parent._get_child_mock(name=entry, _new_name=entry, + _new_parent=parent) + setattr(parent, entry, m) + _set_return_value(parent, m, entry) + return m + + def __get__(self, obj, _type=None): + return self.create_mock() + + + +class _ANY(object): + "A helper object that compares equal to everything." + + def __eq__(self, other): + return True + + def __ne__(self, other): + return False + + def __repr__(self): + return '' + +ANY = _ANY() + + + +def _format_call_signature(name, args, kwargs): + message = '%s(%%s)' % name + formatted_args = '' + args_string = ', '.join([repr(arg) for arg in args]) + kwargs_string = ', '.join([ + '%s=%r' % (key, value) for key, value in kwargs.items() + ]) + if args_string: + formatted_args = args_string + if kwargs_string: + if formatted_args: + formatted_args += ', ' + formatted_args += kwargs_string + + return message % formatted_args + + + +class _Call(tuple): + """ + A tuple for holding the results of a call to a mock, either in the form + `(args, kwargs)` or `(name, args, kwargs)`. + + If args or kwargs are empty then a call tuple will compare equal to + a tuple without those values. This makes comparisons less verbose:: + + _Call(('name', (), {})) == ('name',) + _Call(('name', (1,), {})) == ('name', (1,)) + _Call(((), {'a': 'b'})) == ({'a': 'b'},) + + The `_Call` object provides a useful shortcut for comparing with call:: + + _Call(((1, 2), {'a': 3})) == call(1, 2, a=3) + _Call(('foo', (1, 2), {'a': 3})) == call.foo(1, 2, a=3) + + If the _Call has no name then it will match any name. + """ + def __new__(cls, value=(), name=None, parent=None, two=False, + from_kall=True): + name = '' + args = () + kwargs = {} + _len = len(value) + if _len == 3: + name, args, kwargs = value + elif _len == 2: + first, second = value + if isinstance(first, basestring): + name = first + if isinstance(second, tuple): + args = second + else: + kwargs = second + else: + args, kwargs = first, second + elif _len == 1: + value, = value + if isinstance(value, basestring): + name = value + elif isinstance(value, tuple): + args = value + else: + kwargs = value + + if two: + return tuple.__new__(cls, (args, kwargs)) + + return tuple.__new__(cls, (name, args, kwargs)) + + + def __init__(self, value=(), name=None, parent=None, two=False, + from_kall=True): + self.name = name + self.parent = parent + self.from_kall = from_kall + + + def __eq__(self, other): + if other is ANY: + return True + try: + len_other = len(other) + except TypeError: + return False + + self_name = '' + if len(self) == 2: + self_args, self_kwargs = self + else: + self_name, self_args, self_kwargs = self + + other_name = '' + if len_other == 0: + other_args, other_kwargs = (), {} + elif len_other == 3: + other_name, other_args, other_kwargs = other + elif len_other == 1: + value, = other + if isinstance(value, tuple): + other_args = value + other_kwargs = {} + elif isinstance(value, basestring): + other_name = value + other_args, other_kwargs = (), {} + else: + other_args = () + other_kwargs = value + else: + # len 2 + # could be (name, args) or (name, kwargs) or (args, kwargs) + first, second = other + if isinstance(first, basestring): + other_name = first + if isinstance(second, tuple): + other_args, other_kwargs = second, {} + else: + other_args, other_kwargs = (), second + else: + other_args, other_kwargs = first, second + + if self_name and other_name != self_name: + return False + + # this order is important for ANY to work! + return (other_args, other_kwargs) == (self_args, self_kwargs) + + + def __ne__(self, other): + return not self.__eq__(other) + + + def __call__(self, *args, **kwargs): + if self.name is None: + return _Call(('', args, kwargs), name='()') + + name = self.name + '()' + return _Call((self.name, args, kwargs), name=name, parent=self) + + + def __getattr__(self, attr): + if self.name is None: + return _Call(name=attr, from_kall=False) + name = '%s.%s' % (self.name, attr) + return _Call(name=name, parent=self, from_kall=False) + + + def __repr__(self): + if not self.from_kall: + name = self.name or 'call' + if name.startswith('()'): + name = 'call%s' % name + return name + + if len(self) == 2: + name = 'call' + args, kwargs = self + else: + name, args, kwargs = self + if not name: + name = 'call' + elif not name.startswith('()'): + name = 'call.%s' % name + else: + name = 'call%s' % name + return _format_call_signature(name, args, kwargs) + + + def call_list(self): + """For a call object that represents multiple calls, `call_list` + returns a list of all the intermediate calls as well as the + final call.""" + vals = [] + thing = self + while thing is not None: + if thing.from_kall: + vals.append(thing) + thing = thing.parent + return _CallList(reversed(vals)) + + +call = _Call(from_kall=False) + + + +def create_autospec(spec, spec_set=False, instance=False, _parent=None, + _name=None, **kwargs): + """Create a mock object using another object as a spec. Attributes on the + mock will use the corresponding attribute on the `spec` object as their + spec. + + Functions or methods being mocked will have their arguments checked + to check that they are called with the correct signature. + + If `spec_set` is True then attempting to set attributes that don't exist + on the spec object will raise an `AttributeError`. + + If a class is used as a spec then the return value of the mock (the + instance of the class) will have the same spec. You can use a class as the + spec for an instance object by passing `instance=True`. The returned mock + will only be callable if instances of the mock are callable. + + `create_autospec` also takes arbitrary keyword arguments that are passed to + the constructor of the created mock.""" + if _is_list(spec): + # can't pass a list instance to the mock constructor as it will be + # interpreted as a list of strings + spec = type(spec) + + is_type = isinstance(spec, ClassTypes) + + _kwargs = {'spec': spec} + if spec_set: + _kwargs = {'spec_set': spec} + elif spec is None: + # None we mock with a normal mock without a spec + _kwargs = {} + + _kwargs.update(kwargs) + + Klass = MagicMock + if type(spec) in DescriptorTypes: + # descriptors don't have a spec + # because we don't know what type they return + _kwargs = {} + elif not _callable(spec): + Klass = NonCallableMagicMock + elif is_type and instance and not _instance_callable(spec): + Klass = NonCallableMagicMock + + _new_name = _name + if _parent is None: + # for a top level object no _new_name should be set + _new_name = '' + + mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name, + name=_name, **_kwargs) + + if isinstance(spec, FunctionTypes): + # should only happen at the top level because we don't + # recurse for functions + mock = _set_signature(mock, spec) + else: + _check_signature(spec, mock, is_type, instance) + + if _parent is not None and not instance: + _parent._mock_children[_name] = mock + + if is_type and not instance and 'return_value' not in kwargs: + mock.return_value = create_autospec(spec, spec_set, instance=True, + _name='()', _parent=mock) + + for entry in dir(spec): + if _is_magic(entry): + # MagicMock already does the useful magic methods for us + continue + + if isinstance(spec, FunctionTypes) and entry in FunctionAttributes: + # allow a mock to actually be a function + continue + + # XXXX do we need a better way of getting attributes without + # triggering code execution (?) Probably not - we need the actual + # object to mock it so we would rather trigger a property than mock + # the property descriptor. Likewise we want to mock out dynamically + # provided attributes. + # XXXX what about attributes that raise exceptions other than + # AttributeError on being fetched? + # we could be resilient against it, or catch and propagate the + # exception when the attribute is fetched from the mock + try: + original = getattr(spec, entry) + except AttributeError: + continue + + kwargs = {'spec': original} + if spec_set: + kwargs = {'spec_set': original} + + if not isinstance(original, FunctionTypes): + new = _SpecState(original, spec_set, mock, entry, instance) + mock._mock_children[entry] = new + else: + parent = mock + if isinstance(spec, FunctionTypes): + parent = mock.mock + + new = MagicMock(parent=parent, name=entry, _new_name=entry, + _new_parent=parent, **kwargs) + mock._mock_children[entry] = new + skipfirst = _must_skip(spec, entry, is_type) + _check_signature(original, new, skipfirst=skipfirst) + + # so functions created with _set_signature become instance attributes, + # *plus* their underlying mock exists in _mock_children of the parent + # mock. Adding to _mock_children may be unnecessary where we are also + # setting as an instance attribute? + if isinstance(new, FunctionTypes): + setattr(mock, entry, new) + + return mock + + +def _must_skip(spec, entry, is_type): + if not isinstance(spec, ClassTypes): + if entry in getattr(spec, '__dict__', {}): + # instance attribute - shouldn't skip + return False + spec = spec.__class__ + if not hasattr(spec, '__mro__'): + # old style class: can't have descriptors anyway + return is_type + + for klass in spec.__mro__: + result = klass.__dict__.get(entry, DEFAULT) + if result is DEFAULT: + continue + if isinstance(result, (staticmethod, classmethod)): + return False + return is_type + + # shouldn't get here unless function is a dynamically provided attribute + # XXXX untested behaviour + return is_type + + +def _get_class(obj): + try: + return obj.__class__ + except AttributeError: + # in Python 2, _sre.SRE_Pattern objects have no __class__ + return type(obj) + + +class _SpecState(object): + + def __init__(self, spec, spec_set=False, parent=None, + name=None, ids=None, instance=False): + self.spec = spec + self.ids = ids + self.spec_set = spec_set + self.parent = parent + self.instance = instance + self.name = name + + +FunctionTypes = ( + # python function + type(create_autospec), + # instance method + type(ANY.__eq__), + # unbound method + type(_ANY.__eq__), +) + +FunctionAttributes = set([ + 'func_closure', + 'func_code', + 'func_defaults', + 'func_dict', + 'func_doc', + 'func_globals', + 'func_name', +]) + + +file_spec = None + + +def mock_open(mock=None, read_data=''): + """ + A helper function to create a mock to replace the use of `open`. It works + for `open` called directly or used as a context manager. + + The `mock` argument is the mock object to configure. If `None` (the + default) then a `MagicMock` will be created for you, with the API limited + to methods or attributes available on standard file handles. + + `read_data` is a string for the `read` method of the file handle to return. + This is an empty string by default. + """ + global file_spec + if file_spec is None: + # set on first use + if inPy3k: + import _io + file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) + else: + file_spec = file + + if mock is None: + mock = MagicMock(name='open', spec=open) + + handle = MagicMock(spec=file_spec) + handle.write.return_value = None + handle.__enter__.return_value = handle + handle.read.return_value = read_data + + mock.return_value = handle + return mock + + +class PropertyMock(Mock): + """ + A mock intended to be used as a property, or other descriptor, on a class. + `PropertyMock` provides `__get__` and `__set__` methods so you can specify + a return value when it is fetched. + + Fetching a `PropertyMock` instance from an object calls the mock, with + no args. Setting it calls the mock with the value being set. + """ + def _get_child_mock(self, **kwargs): + return MagicMock(**kwargs) + + def __get__(self, obj, obj_type): + return self() + def __set__(self, obj, val): + self(val) diff --git a/engine/src/flutter/third_party/robolectric/BUILD.gn b/engine/src/flutter/third_party/robolectric/BUILD.gn new file mode 100644 index 0000000000..211c3d63b2 --- /dev/null +++ b/engine/src/flutter/third_party/robolectric/BUILD.gn @@ -0,0 +1,31 @@ +# Copyright 2015 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. + +import("//build/config/android/rules.gni") + +# GYP: //third_party/robolectric/robolectric.gyp:android-all-4.3_r2-robolectric-0 +java_prebuilt("android-all-4.3_r2-robolectric-0") { + jar_path = "lib/android-all-4.3_r2-robolectric-0.jar" +} + +# GYP: //third_party/robolectric/robolectric.gyp:tagsoup-1.2 +java_prebuilt("tagsoup-1.2") { + jar_path = "lib/tagsoup-1.2.jar" +} + +# GYP: //third_party/robolectric/robolectric.gyp:json-20080701 +java_prebuilt("json-20080701") { + jar_path = "lib/json-20080701.jar" +} + +# GYP: //third_party/robolectric/robolectric.gyp:robolectric_jar +java_prebuilt("robolectric_java") { + testonly = true + jar_path = "lib/robolectric-2.4-jar-with-dependencies.jar" + deps = [ + ":android-all-4.3_r2-robolectric-0", + ":tagsoup-1.2", + ":json-20080701", + ] +} diff --git a/engine/src/flutter/third_party/robolectric/LICENSE b/engine/src/flutter/third_party/robolectric/LICENSE new file mode 100644 index 0000000000..f433b1a53f --- /dev/null +++ b/engine/src/flutter/third_party/robolectric/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/engine/src/flutter/third_party/robolectric/OWNERS b/engine/src/flutter/third_party/robolectric/OWNERS new file mode 100644 index 0000000000..bd675fb8c4 --- /dev/null +++ b/engine/src/flutter/third_party/robolectric/OWNERS @@ -0,0 +1,2 @@ +jbudorick@chromium.org +klundberg@chromium.org diff --git a/engine/src/flutter/third_party/robolectric/README.chromium b/engine/src/flutter/third_party/robolectric/README.chromium new file mode 100644 index 0000000000..10aeba31e8 --- /dev/null +++ b/engine/src/flutter/third_party/robolectric/README.chromium @@ -0,0 +1,11 @@ +Name: Robolectric +URL: http://robolectric.org +Version: 2.4 +License: Apache 2.0 +License File: NOT_SHIPPED +Security Critical: no +License Android Compatible: yes +Description: Robolectric is a unit test framework for Android. To update the +robolectric jars, go to robolectric.org/download and follow link to +robolectric-X.X.X-jar-with-dependencies.jar. +Local Modifications: None diff --git a/engine/src/flutter/third_party/robolectric/licenses/extreme.indiana.edu.license.txt b/engine/src/flutter/third_party/robolectric/licenses/extreme.indiana.edu.license.txt new file mode 100644 index 0000000000..c74315554a --- /dev/null +++ b/engine/src/flutter/third_party/robolectric/licenses/extreme.indiana.edu.license.txt @@ -0,0 +1,47 @@ +Indiana University Extreme! Lab Software License + +Version 1.1.1 + +Copyright (c) 2002 Extreme! Lab, Indiana University. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + +3. The end-user documentation included with the redistribution, if any, + must include the following acknowledgment: + + "This product includes software developed by the Indiana University + Extreme! Lab (http://www.extreme.indiana.edu/)." + +Alternately, this acknowledgment may appear in the software itself, +if and wherever such third-party acknowledgments normally appear. + +4. The names "Indiana Univeristy" and "Indiana Univeristy Extreme! Lab" +must not be used to endorse or promote products derived from this +software without prior written permission. For written permission, +please contact http://www.extreme.indiana.edu/. + +5. Products derived from this software may not use "Indiana Univeristy" +name nor may "Indiana Univeristy" appear in their name, without prior +written permission of the Indiana University. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHORS, COPYRIGHT HOLDERS OR ITS CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/engine/src/flutter/third_party/robolectric/licenses/javolution.license.txt b/engine/src/flutter/third_party/robolectric/licenses/javolution.license.txt new file mode 100644 index 0000000000..0633749e4a --- /dev/null +++ b/engine/src/flutter/third_party/robolectric/licenses/javolution.license.txt @@ -0,0 +1,23 @@ +Javolution - Java(TM) Solution for Real-Time and Embedded Systems +Copyright (c) 2006, Javolution (http://javolution.org) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/engine/src/flutter/third_party/robolectric/licenses/pivotal.labs.license.txt b/engine/src/flutter/third_party/robolectric/licenses/pivotal.labs.license.txt new file mode 100644 index 0000000000..c9dccb8d80 --- /dev/null +++ b/engine/src/flutter/third_party/robolectric/licenses/pivotal.labs.license.txt @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2010 Xtreme Labs and Pivotal Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/engine/src/flutter/third_party/robolectric/robolectric.gyp b/engine/src/flutter/third_party/robolectric/robolectric.gyp new file mode 100644 index 0000000000..7c3d785813 --- /dev/null +++ b/engine/src/flutter/third_party/robolectric/robolectric.gyp @@ -0,0 +1,58 @@ +# Copyright 2015 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. + +{ + 'targets': [ + { + # GN: //third_party/robolectric:android-all-4.3_r2-robolectric-0 + 'target_name': 'android-all-4.3_r2-robolectric-0', + 'type': 'none', + 'variables': { + 'jar_path': 'lib/android-all-4.3_r2-robolectric-0.jar', + }, + 'includes': [ + '../../build/host_prebuilt_jar.gypi', + ] + }, + { + # GN: //third_party/robolectric:tagsoup-1.2 + 'target_name': 'tagsoup-1.2', + 'type': 'none', + 'variables': { + 'jar_path': 'lib/tagsoup-1.2.jar', + }, + 'includes': [ + '../../build/host_prebuilt_jar.gypi', + ] + }, + { + # GN: //third_party/robolectric:json-20080701 + 'target_name': 'json-20080701', + 'type': 'none', + 'variables': { + 'jar_path': 'lib/json-20080701.jar', + }, + 'includes': [ + '../../build/host_prebuilt_jar.gypi', + ] + }, + { + # GN: //third_party/robolectric:robolectric_java + 'target_name': 'robolectric_jar', + 'type': 'none', + 'dependencies': [ + 'android-all-4.3_r2-robolectric-0', + 'tagsoup-1.2', + 'json-20080701', + ], + 'variables': { + 'jar_path': 'lib/robolectric-2.4-jar-with-dependencies.jar', + }, + 'includes': [ + '../../build/host_prebuilt_jar.gypi', + ] + }, + ], +} + diff --git a/engine/src/flutter/third_party/smhasher/BUILD.gn b/engine/src/flutter/third_party/smhasher/BUILD.gn new file mode 100644 index 0000000000..fd9088c271 --- /dev/null +++ b/engine/src/flutter/third_party/smhasher/BUILD.gn @@ -0,0 +1,30 @@ +# Copyright 2014 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. + +source_set("murmurhash3") { + sources = [ + "src/MurmurHash3.cpp", + "src/MurmurHash3.h", + ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} + +source_set("pmurhash") { + sources = [ + "src/PMurHash.c", + "src/PMurHash.h", + ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} + +source_set("cityhash") { + sources = [ + "src/City.cpp", + "src/City.h", + ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] +} diff --git a/engine/src/flutter/third_party/smhasher/LICENSE b/engine/src/flutter/third_party/smhasher/LICENSE new file mode 100644 index 0000000000..3f18a844ad --- /dev/null +++ b/engine/src/flutter/third_party/smhasher/LICENSE @@ -0,0 +1,23 @@ +All MurmurHash source files are placed in the public domain. + +The license below applies to all other code in SMHasher: + +Copyright (c) 2011 Google, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/engine/src/flutter/third_party/smhasher/README.chromium b/engine/src/flutter/third_party/smhasher/README.chromium new file mode 100644 index 0000000000..b84ea3249b --- /dev/null +++ b/engine/src/flutter/third_party/smhasher/README.chromium @@ -0,0 +1,14 @@ +Name: SMHasher +URL: http://code.google.com/p/smhasher/ +Version: 0 +Revision: 147 +License: MIT, Public Domain +License File: LICENSE +Security Critical: yes + +Description: +This is a library containing the MurmurHash3 function, and a hashing function +test suite. + +Licenses are MIT (SMHasher) and Public Domain (MurmurHash). + diff --git a/engine/src/flutter/third_party/smhasher/smhasher.gyp b/engine/src/flutter/third_party/smhasher/smhasher.gyp new file mode 100644 index 0000000000..cad864e38b --- /dev/null +++ b/engine/src/flutter/third_party/smhasher/smhasher.gyp @@ -0,0 +1,36 @@ +# Copyright (c) 2012 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. + +{ + 'targets': [ + { + 'target_name': 'murmurhash3', + 'type': 'static_library', + 'sources': [ + 'src/MurmurHash3.h', + 'src/MurmurHash3.cpp', + ], + }, + { + 'target_name': 'pmurhash', + 'type': 'static_library', + 'sources': [ + 'src/PMurHash.h', + 'src/PMurHash.c', + ], + }, + { + 'target_name': 'cityhash', + 'type': 'static_library', + 'sources': [ + 'src/City.cpp', + 'src/City.h', + ], + # TODO(jschuh): http://code.google.com/p/smhasher/issues/detail?id=19 + 'msvs_disabled_warnings': [ + 4267, + ], + }, + ], +} diff --git a/engine/src/flutter/tools/checkperms/OWNERS b/engine/src/flutter/tools/checkperms/OWNERS new file mode 100644 index 0000000000..1967bf567e --- /dev/null +++ b/engine/src/flutter/tools/checkperms/OWNERS @@ -0,0 +1 @@ +thestig@chromium.org diff --git a/engine/src/flutter/tools/checkperms/PRESUBMIT.py b/engine/src/flutter/tools/checkperms/PRESUBMIT.py new file mode 100644 index 0000000000..c2f93567da --- /dev/null +++ b/engine/src/flutter/tools/checkperms/PRESUBMIT.py @@ -0,0 +1,27 @@ +# Copyright (c) 2012 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. + +"""Top-level presubmit script for checkperms. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for +details on the presubmit API built into gcl. +""" + + +def CommonChecks(input_api, output_api): + output = [] + output.extend(input_api.canned_checks.RunPylint(input_api, output_api)) + # Run it like if it were a unit test. + output.extend( + input_api.canned_checks.RunUnitTests( + input_api, output_api, ['./checkperms.py'])) + return output + + +def CheckChangeOnUpload(input_api, output_api): + return CommonChecks(input_api, output_api) + + +def CheckChangeOnCommit(input_api, output_api): + return CommonChecks(input_api, output_api) diff --git a/engine/src/flutter/tools/checkperms/checkperms.py b/engine/src/flutter/tools/checkperms/checkperms.py new file mode 100755 index 0000000000..e2807c73c5 --- /dev/null +++ b/engine/src/flutter/tools/checkperms/checkperms.py @@ -0,0 +1,480 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Makes sure files have the right permissions. + +Some developers have broken SCM configurations that flip the executable +permission on for no good reason. Unix developers who run ls --color will then +see .cc files in green and get confused. + +- For file extensions that must be executable, add it to EXECUTABLE_EXTENSIONS. +- For file extensions that must not be executable, add it to + NOT_EXECUTABLE_EXTENSIONS. +- To ignore all the files inside a directory, add it to IGNORED_PATHS. +- For file base name with ambiguous state and that should not be checked for + shebang, add it to IGNORED_FILENAMES. + +Any file not matching the above will be opened and looked if it has a shebang +or an ELF header. If this does not match the executable bit on the file, the +file will be flagged. + +Note that all directory separators must be slashes (Unix-style) and not +backslashes. All directories should be relative to the source root and all +file paths should be only lowercase. +""" + +import json +import logging +import optparse +import os +import stat +import string +import subprocess +import sys + +#### USER EDITABLE SECTION STARTS HERE #### + +# Files with these extensions must have executable bit set. +# +# Case-sensitive. +EXECUTABLE_EXTENSIONS = ( + 'bat', + 'dll', + 'dylib', + 'exe', +) + +# These files must have executable bit set. +# +# Case-insensitive, lower-case only. +EXECUTABLE_PATHS = ( + 'chrome/test/data/app_shim/app_shim_32_bit.app/contents/' + 'macos/app_mode_loader', + 'chrome/test/data/extensions/uitest/plugins/plugin.plugin/contents/' + 'macos/testnetscapeplugin', + 'chrome/test/data/extensions/uitest/plugins_private/plugin.plugin/contents/' + 'macos/testnetscapeplugin', +) + +# These files must not have the executable bit set. This is mainly a performance +# optimization as these files are not checked for shebang. The list was +# partially generated from: +# git ls-files | grep "\\." | sed 's/.*\.//' | sort | uniq -c | sort -b -g +# +# Case-sensitive. +NON_EXECUTABLE_EXTENSIONS = ( + '1', + '3ds', + 'S', + 'am', + 'applescript', + 'asm', + 'c', + 'cc', + 'cfg', + 'chromium', + 'cpp', + 'crx', + 'cs', + 'css', + 'cur', + 'def', + 'der', + 'expected', + 'gif', + 'grd', + 'gyp', + 'gypi', + 'h', + 'hh', + 'htm', + 'html', + 'hyph', + 'ico', + 'idl', + 'java', + 'jpg', + 'js', + 'json', + 'm', + 'm4', + 'mm', + 'mms', + 'mock-http-headers', + 'nexe', + 'nmf', + 'onc', + 'pat', + 'patch', + 'pdf', + 'pem', + 'plist', + 'png', + 'proto', + 'rc', + 'rfx', + 'rgs', + 'rules', + 'spec', + 'sql', + 'srpc', + 'svg', + 'tcl', + 'test', + 'tga', + 'txt', + 'vcproj', + 'vsprops', + 'webm', + 'word', + 'xib', + 'xml', + 'xtb', + 'zip', +) + +# These files must not have executable bit set. +# +# Case-insensitive, lower-case only. +NON_EXECUTABLE_PATHS = ( + 'build/android/tests/symbolize/liba.so', + 'build/android/tests/symbolize/libb.so', + 'chrome/installer/mac/sign_app.sh.in', + 'chrome/installer/mac/sign_versioned_dir.sh.in', + 'chrome/test/data/extensions/uitest/plugins/plugin32.so', + 'chrome/test/data/extensions/uitest/plugins/plugin64.so', + 'chrome/test/data/extensions/uitest/plugins_private/plugin32.so', + 'chrome/test/data/extensions/uitest/plugins_private/plugin64.so', + 'components/test/data/component_updater/ihfokbkgjpifnbbojhneepfflplebdkc/' + 'ihfokbkgjpifnbbojhneepfflplebdkc_1/a_changing_binary_file', + 'components/test/data/component_updater/ihfokbkgjpifnbbojhneepfflplebdkc/' + 'ihfokbkgjpifnbbojhneepfflplebdkc_2/a_changing_binary_file', + 'courgette/testdata/elf-32-1', + 'courgette/testdata/elf-32-2', + 'courgette/testdata/elf-64', +) + +# File names that are always whitelisted. (These are mostly autoconf spew.) +# +# Case-sensitive. +IGNORED_FILENAMES = ( + 'config.guess', + 'config.sub', + 'configure', + 'depcomp', + 'install-sh', + 'missing', + 'mkinstalldirs', + 'naclsdk', + 'scons', +) + +# File paths starting with one of these will be ignored as well. +# Please consider fixing your file permissions, rather than adding to this list. +# +# Case-insensitive, lower-case only. +IGNORED_PATHS = ( + 'native_client_sdk/src/build_tools/sdk_tools/third_party/fancy_urllib/' + '__init__.py', + 'out/', + # TODO(maruel): Fix these. + 'third_party/android_testrunner/', + 'third_party/bintrees/', + 'third_party/closure_linter/', + 'third_party/devscripts/licensecheck.pl.vanilla', + 'third_party/hyphen/', + 'third_party/jemalloc/', + 'third_party/lcov-1.9/contrib/galaxy/conglomerate_functions.pl', + 'third_party/lcov-1.9/contrib/galaxy/gen_makefile.sh', + 'third_party/lcov/contrib/galaxy/conglomerate_functions.pl', + 'third_party/lcov/contrib/galaxy/gen_makefile.sh', + 'third_party/libevent/autogen.sh', + 'third_party/libevent/test/test.sh', + 'third_party/libxml/linux/xml2-config', + 'third_party/libxml/src/ltmain.sh', + 'third_party/mesa/', + 'third_party/protobuf/', + 'third_party/python_gflags/gflags.py', + 'third_party/sqlite/', + 'third_party/talloc/script/mksyms.sh', + 'third_party/tcmalloc/', + 'third_party/tlslite/setup.py', +) + +#### USER EDITABLE SECTION ENDS HERE #### + +assert set(EXECUTABLE_EXTENSIONS) & set(NON_EXECUTABLE_EXTENSIONS) == set() +assert set(EXECUTABLE_PATHS) & set(NON_EXECUTABLE_PATHS) == set() + +VALID_CHARS = set(string.ascii_lowercase + string.digits + '/-_.') +for paths in (EXECUTABLE_PATHS, NON_EXECUTABLE_PATHS, IGNORED_PATHS): + assert all([set(path).issubset(VALID_CHARS) for path in paths]) + + +def capture(cmd, cwd): + """Returns the output of a command. + + Ignores the error code or stderr. + """ + logging.debug('%s; cwd=%s' % (' '.join(cmd), cwd)) + env = os.environ.copy() + env['LANGUAGE'] = 'en_US.UTF-8' + p = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, env=env) + return p.communicate()[0] + + +def get_git_root(dir_path): + """Returns the git checkout root or None.""" + root = capture(['git', 'rev-parse', '--show-toplevel'], dir_path).strip() + if root: + return root + + +def is_ignored(rel_path): + """Returns True if rel_path is in our whitelist of files to ignore.""" + rel_path = rel_path.lower() + return ( + os.path.basename(rel_path) in IGNORED_FILENAMES or + rel_path.lower().startswith(IGNORED_PATHS)) + + +def must_be_executable(rel_path): + """The file name represents a file type that must have the executable bit + set. + """ + return (os.path.splitext(rel_path)[1][1:] in EXECUTABLE_EXTENSIONS or + rel_path.lower() in EXECUTABLE_PATHS) + + +def must_not_be_executable(rel_path): + """The file name represents a file type that must not have the executable + bit set. + """ + return (os.path.splitext(rel_path)[1][1:] in NON_EXECUTABLE_EXTENSIONS or + rel_path.lower() in NON_EXECUTABLE_PATHS) + + +def has_executable_bit(full_path): + """Returns if any executable bit is set.""" + permission = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + return bool(permission & os.stat(full_path).st_mode) + + +def has_shebang_or_is_elf(full_path): + """Returns if the file starts with #!/ or is an ELF binary. + + full_path is the absolute path to the file. + """ + with open(full_path, 'rb') as f: + data = f.read(4) + return (data[:3] == '#!/' or data == '#! /', data == '\x7fELF') + + +def check_file(root_path, rel_path): + """Checks the permissions of the file whose path is root_path + rel_path and + returns an error if it is inconsistent. Returns None on success. + + It is assumed that the file is not ignored by is_ignored(). + + If the file name is matched with must_be_executable() or + must_not_be_executable(), only its executable bit is checked. + Otherwise, the first few bytes of the file are read to verify if it has a + shebang or ELF header and compares this with the executable bit on the file. + """ + full_path = os.path.join(root_path, rel_path) + def result_dict(error): + return { + 'error': error, + 'full_path': full_path, + 'rel_path': rel_path, + } + try: + bit = has_executable_bit(full_path) + except OSError: + # It's faster to catch exception than call os.path.islink(). Chromium + # tree happens to have invalid symlinks under + # third_party/openssl/openssl/test/. + return None + + if must_be_executable(rel_path): + if not bit: + return result_dict('Must have executable bit set') + return + if must_not_be_executable(rel_path): + if bit: + return result_dict('Must not have executable bit set') + return + + # For the others, it depends on the file header. + (shebang, elf) = has_shebang_or_is_elf(full_path) + if bit != (shebang or elf): + if bit: + return result_dict('Has executable bit but not shebang or ELF header') + if shebang: + return result_dict('Has shebang but not executable bit') + return result_dict('Has ELF header but not executable bit') + + +def check_files(root, files): + gen = (check_file(root, f) for f in files if not is_ignored(f)) + return filter(None, gen) + + +class ApiBase(object): + def __init__(self, root_dir, bare_output): + self.root_dir = root_dir + self.bare_output = bare_output + self.count = 0 + self.count_read_header = 0 + + def check_file(self, rel_path): + logging.debug('check_file(%s)' % rel_path) + self.count += 1 + + if (not must_be_executable(rel_path) and + not must_not_be_executable(rel_path)): + self.count_read_header += 1 + + return check_file(self.root_dir, rel_path) + + def check_dir(self, rel_path): + return self.check(rel_path) + + def check(self, start_dir): + """Check the files in start_dir, recursively check its subdirectories.""" + errors = [] + items = self.list_dir(start_dir) + logging.info('check(%s) -> %d' % (start_dir, len(items))) + for item in items: + full_path = os.path.join(self.root_dir, start_dir, item) + rel_path = full_path[len(self.root_dir) + 1:] + if is_ignored(rel_path): + continue + if os.path.isdir(full_path): + # Depth first. + errors.extend(self.check_dir(rel_path)) + else: + error = self.check_file(rel_path) + if error: + errors.append(error) + return errors + + def list_dir(self, start_dir): + """Lists all the files and directory inside start_dir.""" + return sorted( + x for x in os.listdir(os.path.join(self.root_dir, start_dir)) + if not x.startswith('.') + ) + + +class ApiAllFilesAtOnceBase(ApiBase): + _files = None + + def list_dir(self, start_dir): + """Lists all the files and directory inside start_dir.""" + if self._files is None: + self._files = sorted(self._get_all_files()) + if not self.bare_output: + print 'Found %s files' % len(self._files) + start_dir = start_dir[len(self.root_dir) + 1:] + return [ + x[len(start_dir):] for x in self._files if x.startswith(start_dir) + ] + + def _get_all_files(self): + """Lists all the files and directory inside self._root_dir.""" + raise NotImplementedError() + + +class ApiGit(ApiAllFilesAtOnceBase): + def _get_all_files(self): + return capture(['git', 'ls-files'], cwd=self.root_dir).splitlines() + + +def get_scm(dir_path, bare): + """Returns a properly configured ApiBase instance.""" + cwd = os.getcwd() + root = get_git_root(dir_path or cwd) + if root: + if not bare: + print('Found git repository at %s' % root) + return ApiGit(dir_path or root, bare) + + # Returns a non-scm aware checker. + if not bare: + print('Failed to determine the SCM for %s' % dir_path) + return ApiBase(dir_path or cwd, bare) + + +def main(): + usage = """Usage: python %prog [--root ] [tocheck] + tocheck Specifies the directory, relative to root, to check. This defaults + to "." so it checks everything. + +Examples: + python %prog + python %prog --root /path/to/source chrome""" + + parser = optparse.OptionParser(usage=usage) + parser.add_option( + '--root', + help='Specifies the repository root. This defaults ' + 'to the checkout repository root') + parser.add_option( + '-v', '--verbose', action='count', default=0, help='Print debug logging') + parser.add_option( + '--bare', + action='store_true', + default=False, + help='Prints the bare filename triggering the checks') + parser.add_option( + '--file', action='append', dest='files', + help='Specifics a list of files to check the permissions of. Only these ' + 'files will be checked') + parser.add_option('--json', help='Path to JSON output file') + options, args = parser.parse_args() + + levels = [logging.ERROR, logging.INFO, logging.DEBUG] + logging.basicConfig(level=levels[min(len(levels) - 1, options.verbose)]) + + if len(args) > 1: + parser.error('Too many arguments used') + + if options.root: + options.root = os.path.abspath(options.root) + + if options.files: + # --file implies --bare (for PRESUBMIT.py). + options.bare = True + + errors = check_files(options.root, options.files) + else: + api = get_scm(options.root, options.bare) + start_dir = args[0] if args else api.root_dir + errors = api.check(start_dir) + + if not options.bare: + print('Processed %s files, %d files where tested for shebang/ELF ' + 'header' % (api.count, api.count_read_header)) + + if options.json: + with open(options.json, 'w') as f: + json.dump(errors, f) + + if errors: + if options.bare: + print '\n'.join(e['full_path'] for e in errors) + else: + print '\nFAILED\n' + print '\n'.join('%s: %s' % (e['full_path'], e['error']) for e in errors) + return 1 + if not options.bare: + print '\nSUCCESS\n' + return 0 + + +if '__main__' == __name__: + sys.exit(main()) diff --git a/engine/src/flutter/tools/dart/update.py b/engine/src/flutter/tools/dart/update.py new file mode 100755 index 0000000000..b830528e90 --- /dev/null +++ b/engine/src/flutter/tools/dart/update.py @@ -0,0 +1,101 @@ +#!/usr/bin/python +# Copyright 2015 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. + +"""Pulls down the current dart sdk to third_party/dart-sdk/. + +You can manually force this to run again by removing +third_party/dart-sdk/STAMP_FILE, which contains the URL of the SDK that +was downloaded. Rolling works by updating LINUX_64_SDK to a new URL. +""" + +import os +import shutil +import subprocess +import sys + +# How to roll the dart sdk: Just change this url! We write this to the stamp +# file after we download, and then check the stamp file for differences. +SDK_URL_BASE = ('http://gsdview.appspot.com/dart-archive/channels/dev/raw/' + '1.12.0-dev.3.1/sdk/') + +LINUX_64_SDK = 'dartsdk-linux-x64-release.zip' +MACOS_64_SDK = 'dartsdk-macos-x64-release.zip' + +# Path constants. (All of these should be absolute paths.) +THIS_DIR = os.path.abspath(os.path.dirname(__file__)) +MOJO_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..')) +DART_SDK_DIR = os.path.join(MOJO_DIR, 'third_party', 'dart-sdk') +STAMP_FILE = os.path.join(DART_SDK_DIR, 'STAMP_FILE') +LIBRARIES_FILE = os.path.join(DART_SDK_DIR,'dart-sdk', + 'lib', '_internal', 'libraries.dart') +PATCH_FILE = os.path.join(MOJO_DIR, 'tools', 'dart', 'patch_sdk.diff') + +def RunCommand(command, fail_hard=True): + """Run command and return success (True) or failure; or if fail_hard is + True, exit on failure.""" + + print 'Running %s' % (str(command)) + if subprocess.call(command, shell=False) == 0: + return True + print 'Failed.' + if fail_hard: + sys.exit(1) + return False + +def main(): + # Only get the SDK if we don't have a stamp for or have an out of date stamp + # file. + get_sdk = False + if sys.platform.startswith('linux'): + sdk_url = SDK_URL_BASE + LINUX_64_SDK + output_file = os.path.join(DART_SDK_DIR, LINUX_64_SDK) + elif sys.platform.startswith('darwin'): + sdk_url = SDK_URL_BASE + MACOS_64_SDK + output_file = os.path.join(DART_SDK_DIR, MACOS_64_SDK) + else: + print "Platform not supported" + return 1 + + if not os.path.exists(STAMP_FILE): + get_sdk = True + else: + # Get the contents of the stamp file. + with open(STAMP_FILE, "r") as stamp_file: + stamp_url = stamp_file.read().replace('\n', '') + if stamp_url != sdk_url: + get_sdk = True + + if get_sdk: + # Completely remove all traces of the previous SDK. + if os.path.exists(DART_SDK_DIR): + shutil.rmtree(DART_SDK_DIR) + os.mkdir(DART_SDK_DIR) + + # Download the Linux x64 based Dart SDK. + # '-C -': Resume transfer if possible. + # '--location': Follow Location: redirects. + # '-o': Output file. + curl_command = ['curl', + '-C', '-', + '--location', + '-o', output_file, + sdk_url] + if not RunCommand(curl_command, fail_hard=False): + print "Failed to get dart sdk from server." + return 1 + + # Write our stamp file so we don't redownload the sdk. + with open(STAMP_FILE, "w") as stamp_file: + stamp_file.write(sdk_url) + + unzip_command = ['unzip', '-o', '-q', output_file, '-d', DART_SDK_DIR] + if not RunCommand(unzip_command, fail_hard=False): + print "Failed to unzip the dart sdk." + return 1 + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/find_depot_tools.py b/engine/src/flutter/tools/find_depot_tools.py new file mode 100644 index 0000000000..1f91866279 --- /dev/null +++ b/engine/src/flutter/tools/find_depot_tools.py @@ -0,0 +1,45 @@ +# Copyright (c) 2011 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. +"""Small utility function to find depot_tools and add it to the python path. + +Will throw an ImportError exception if depot_tools can't be found since it +imports breakpad. +""" + +import os +import sys + + +def IsRealDepotTools(path): + return os.path.isfile(os.path.join(path, 'gclient.py')) + + +def add_depot_tools_to_path(): + """Search for depot_tools and add it to sys.path.""" + # First look if depot_tools is already in PYTHONPATH. + for i in sys.path: + if i.rstrip(os.sep).endswith('depot_tools') and IsRealDepotTools(i): + return i + # Then look if depot_tools is in PATH, common case. + for i in os.environ['PATH'].split(os.pathsep): + if IsRealDepotTools(i): + sys.path.append(i.rstrip(os.sep)) + return i + # Rare case, it's not even in PATH, look upward up to root. + root_dir = os.path.dirname(os.path.abspath(__file__)) + previous_dir = os.path.abspath(__file__) + while root_dir and root_dir != previous_dir: + i = os.path.join(root_dir, 'depot_tools') + if IsRealDepotTools(i): + sys.path.append(i) + return i + previous_dir = root_dir + root_dir = os.path.dirname(root_dir) + print >> sys.stderr, 'Failed to find depot_tools' + return None + +add_depot_tools_to_path() + +# pylint: disable=W0611 +import breakpad diff --git a/engine/src/flutter/tools/gdb/gdb_chrome.py b/engine/src/flutter/tools/gdb/gdb_chrome.py new file mode 100644 index 0000000000..2ba7ce8b1d --- /dev/null +++ b/engine/src/flutter/tools/gdb/gdb_chrome.py @@ -0,0 +1,331 @@ +# Copyright (c) 2011 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. + +"""GDB support for Chrome types. + +Add this to your gdb by amending your ~/.gdbinit as follows: + python + import sys + sys.path.insert(0, "/path/to/tools/gdb/") + import gdb_chrome + end + +Use + (gdb) p /r any_variable +to print |any_variable| without using any printers. +""" + +import datetime +import gdb +import gdb.printing +import os +import sys + +sys.path.insert(0, os.path.join( + os.path.dirname(os.path.abspath(__file__)), + '..', '..', 'third_party', 'WebKit', 'Tools', 'gdb')) +try: + import webkit +finally: + sys.path.pop(0) + +# When debugging this module, set the below variable to True, and then use +# (gdb) python del sys.modules['gdb_chrome'] +# (gdb) python import gdb_chrome +# to reload. +_DEBUGGING = False + + +pp_set = gdb.printing.RegexpCollectionPrettyPrinter("chromium") + + +def typed_ptr(ptr): + """Prints a pointer along with its exact type. + + By default, gdb would print just the address, which takes more + steps to interpret. + """ + # Returning this as a cast expression surrounded by parentheses + # makes it easier to cut+paste inside of gdb. + return '((%s)%s)' % (ptr.dynamic_type, ptr) + + +def yield_fields(val): + """Use this in a printer's children() method to print an object's fields. + + e.g. + def children(): + for result in yield_fields(self.val): + yield result + """ + try: + fields = val.type.target().fields() + except: + fields = val.type.fields() + for field in fields: + if field.is_base_class: + yield (field.name, val.cast(gdb.lookup_type(field.name))) + else: + yield (field.name, val[field.name]) + + +class Printer(object): + def __init__(self, val): + self.val = val + + +class StringPrinter(Printer): + def display_hint(self): + return 'string' + + +class String16Printer(StringPrinter): + def to_string(self): + return webkit.ustring_to_string(self.val['_M_dataplus']['_M_p']) +pp_set.add_printer( + 'string16', + '^string16|std::basic_string<(unsigned short|base::char16).*>$', + String16Printer); + + +class GURLPrinter(StringPrinter): + def to_string(self): + return self.val['spec_'] +pp_set.add_printer('GURL', '^GURL$', GURLPrinter) + + +class FilePathPrinter(StringPrinter): + def to_string(self): + return self.val['path_']['_M_dataplus']['_M_p'] +pp_set.add_printer('FilePath', '^FilePath$', FilePathPrinter) + + +class SizePrinter(Printer): + def to_string(self): + return '%sx%s' % (self.val['width_'], self.val['height_']) +pp_set.add_printer('gfx::Size', '^gfx::(Size|SizeF|SizeBase<.*>)$', SizePrinter) + + +class PointPrinter(Printer): + def to_string(self): + return '%s,%s' % (self.val['x_'], self.val['y_']) +pp_set.add_printer('gfx::Point', '^gfx::(Point|PointF|PointBase<.*>)$', + PointPrinter) + + +class RectPrinter(Printer): + def to_string(self): + return '%s %s' % (self.val['origin_'], self.val['size_']) +pp_set.add_printer('gfx::Rect', '^gfx::(Rect|RectF|RectBase<.*>)$', + RectPrinter) + + +class SmartPtrPrinter(Printer): + def to_string(self): + return '%s%s' % (self.typename, typed_ptr(self.ptr())) + + +class ScopedRefPtrPrinter(SmartPtrPrinter): + typename = 'scoped_refptr' + def ptr(self): + return self.val['ptr_'] +pp_set.add_printer('scoped_refptr', '^scoped_refptr<.*>$', ScopedRefPtrPrinter) + + +class LinkedPtrPrinter(SmartPtrPrinter): + typename = 'linked_ptr' + def ptr(self): + return self.val['value_'] +pp_set.add_printer('linked_ptr', '^linked_ptr<.*>$', LinkedPtrPrinter) + + +class WeakPtrPrinter(SmartPtrPrinter): + typename = 'base::WeakPtr' + def ptr(self): + flag = ScopedRefPtrPrinter(self.val['ref_']['flag_']).ptr() + if flag and flag['is_valid_']: + return self.val['ptr_'] + return gdb.Value(0).cast(self.val['ptr_'].type) +pp_set.add_printer('base::WeakPtr', '^base::WeakPtr<.*>$', WeakPtrPrinter) + + +class CallbackPrinter(Printer): + """Callbacks provide no usable information so reduce the space they take.""" + def to_string(self): + return '...' +pp_set.add_printer('base::Callback', '^base::Callback<.*>$', CallbackPrinter) + + +class LocationPrinter(Printer): + def to_string(self): + return '%s()@%s:%s' % (self.val['function_name_'].string(), + self.val['file_name_'].string(), + self.val['line_number_']) +pp_set.add_printer('tracked_objects::Location', '^tracked_objects::Location$', + LocationPrinter) + + +class PendingTaskPrinter(Printer): + def to_string(self): + return 'From %s' % (self.val['posted_from'],) + + def children(self): + for result in yield_fields(self.val): + if result[0] not in ('task', 'posted_from'): + yield result +pp_set.add_printer('base::PendingTask', '^base::PendingTask$', + PendingTaskPrinter) + + +class LockPrinter(Printer): + def to_string(self): + try: + if self.val['owned_by_thread_']: + return 'Locked by thread %s' % self.val['owning_thread_id_'] + else: + return 'Unlocked' + except gdb.error: + return 'Unknown state' +pp_set.add_printer('base::Lock', '^base::Lock$', LockPrinter) + + +class TimeDeltaPrinter(object): + def __init__(self, val): + self._timedelta = datetime.timedelta(microseconds=int(val['delta_'])) + + def timedelta(self): + return self._timedelta + + def to_string(self): + return str(self._timedelta) +pp_set.add_printer('base::TimeDelta', '^base::TimeDelta$', TimeDeltaPrinter) + + +class TimeTicksPrinter(TimeDeltaPrinter): + def __init__(self, val): + self._timedelta = datetime.timedelta(microseconds=int(val['ticks_'])) +pp_set.add_printer('base::TimeTicks', '^base::TimeTicks$', TimeTicksPrinter) + + +class TimePrinter(object): + def __init__(self, val): + timet_offset = gdb.parse_and_eval( + 'base::Time::kTimeTToMicrosecondsOffset') + self._datetime = (datetime.datetime.fromtimestamp(0) + + datetime.timedelta(microseconds= + int(val['us_'] - timet_offset))) + + def datetime(self): + return self._datetime + + def to_string(self): + return str(self._datetime) +pp_set.add_printer('base::Time', '^base::Time$', TimePrinter) + + +class IpcMessagePrinter(Printer): + def header(self): + return self.val['header_'].cast( + gdb.lookup_type('IPC::Message::Header').pointer()) + + def to_string(self): + message_type = self.header()['type'] + return '%s of kind %s line %s' % ( + self.val.dynamic_type, + (message_type >> 16).cast(gdb.lookup_type('IPCMessageStart')), + message_type & 0xffff) + + def children(self): + yield ('header_', self.header().dereference()) + yield ('capacity_after_header_', self.val['capacity_after_header_']) + for field in self.val.type.fields(): + if field.is_base_class: + continue + yield (field.name, self.val[field.name]) +pp_set.add_printer('IPC::Message', '^IPC::Message$', IpcMessagePrinter) + + +class NotificationRegistrarPrinter(Printer): + def to_string(self): + try: + registrations = self.val['registered_'] + vector_finish = registrations['_M_impl']['_M_finish'] + vector_start = registrations['_M_impl']['_M_start'] + if vector_start == vector_finish: + return 'Not watching notifications' + if vector_start.dereference().type.sizeof == 0: + # Incomplete type: b/8242773 + return 'Watching some notifications' + return ('Watching %s notifications; ' + 'print %s->registered_ for details') % ( + int(vector_finish - vector_start), + typed_ptr(self.val.address)) + except gdb.error: + return 'NotificationRegistrar' +pp_set.add_printer('content::NotificationRegistrar', + '^content::NotificationRegistrar$', + NotificationRegistrarPrinter) + + +class SiteInstanceImplPrinter(object): + def __init__(self, val): + self.val = val.cast(val.dynamic_type) + + def to_string(self): + return 'SiteInstanceImpl@%s for %s' % ( + self.val.address, self.val['site_']) + + def children(self): + yield ('id_', self.val['id_']) + yield ('has_site_', self.val['has_site_']) + if self.val['browsing_instance_']['ptr_']: + yield ('browsing_instance_', self.val['browsing_instance_']['ptr_']) + if self.val['process_']: + yield ('process_', typed_ptr(self.val['process_'])) +pp_set.add_printer('content::SiteInstanceImpl', '^content::SiteInstanceImpl$', + SiteInstanceImplPrinter) + + +class RenderProcessHostImplPrinter(object): + def __init__(self, val): + self.val = val.cast(val.dynamic_type) + + def to_string(self): + pid = '' + try: + child_process_launcher_ptr = ( + self.val['child_process_launcher_']['impl_']['data_']['ptr']) + if child_process_launcher_ptr: + context = (child_process_launcher_ptr['context_']['ptr_']) + if context: + pid = ' PID %s' % str(context['process_']['process_']) + except gdb.error: + # The definition of the Context type may not be available. + # b/8242773 + pass + return 'RenderProcessHostImpl@%s%s' % (self.val.address, pid) + + def children(self): + yield ('id_', self.val['id_']) + yield ('listeners_', + self.val['listeners_']['data_']) + yield ('worker_ref_count_', self.val['worker_ref_count_']) + yield ('fast_shutdown_started_', self.val['fast_shutdown_started_']) + yield ('deleting_soon_', self.val['deleting_soon_']) + yield ('pending_views_', self.val['pending_views_']) + yield ('visible_widgets_', self.val['visible_widgets_']) + yield ('backgrounded_', self.val['backgrounded_']) + yield ('widget_helper_', self.val['widget_helper_']) + yield ('is_initialized_', self.val['is_initialized_']) + yield ('browser_context_', typed_ptr(self.val['browser_context_'])) + yield ('sudden_termination_allowed_', + self.val['sudden_termination_allowed_']) + yield ('ignore_input_events_', self.val['ignore_input_events_']) + yield ('is_guest_', self.val['is_guest_']) +pp_set.add_printer('content::RenderProcessHostImpl', + '^content::RenderProcessHostImpl$', + RenderProcessHostImplPrinter) + + +gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING) diff --git a/engine/src/flutter/tools/generate_library_loader/generate_library_loader.gni b/engine/src/flutter/tools/generate_library_loader/generate_library_loader.gni new file mode 100644 index 0000000000..24f753bac7 --- /dev/null +++ b/engine/src/flutter/tools/generate_library_loader/generate_library_loader.gni @@ -0,0 +1,70 @@ +# Copyright 2014 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. + +# This template makes a stub for a Linux system library that dynamically loads +# it at runtime. + +# name: Name to use for the value of the --name arg. +# output_h/output_cc: Names for the generated header/cc file with no dir. +# header: header file to process. Example: "" +# functions: List of strings for functions to process. +# config: (optional) Label of the config generated by pkgconfig. +# bundled_header: (optional) +template("generate_library_loader") { + output_h = "$root_gen_dir/library_loaders/" + invoker.output_h + output_cc = "$root_gen_dir/library_loaders/" + invoker.output_cc + + action_visibility = [ ":$target_name" ] + action("${target_name}_loader") { + visibility = action_visibility + + script = "//tools/generate_library_loader/generate_library_loader.py" + if (defined(invoker.visibility)) { + visibility = invoker.visibility + } + + outputs = [ + output_h, + output_cc, + ] + + args = [ + "--name", + invoker.name, + "--output-h", + rebase_path(output_h), + "--output-cc", + rebase_path(output_cc), + "--header", + invoker.header, + + # Note GYP build exposes a per-target variable to control this, which, if + # manually set to true, will disable dlopen(). Its not clear this is + # needed, so here we just leave off. If this can be done globally, we can + # expose one switch for this value, otherwise we need to add a template + # param for this. + "--link-directly=0", + ] + if (defined(invoker.bundled_header)) { + args += [ + "--bundled-header", + invoker.bundled_header, + ] + } + args += invoker.functions + } + + source_set(target_name) { + if (defined(invoker.config)) { + public_configs = [ invoker.config ] + } + sources = [ + output_cc, + output_h, + ] + deps = [ + ":${target_name}_loader", + ] + } +} diff --git a/engine/src/flutter/tools/generate_library_loader/generate_library_loader.py b/engine/src/flutter/tools/generate_library_loader/generate_library_loader.py new file mode 100755 index 0000000000..5e9da2c8de --- /dev/null +++ b/engine/src/flutter/tools/generate_library_loader/generate_library_loader.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +""" +Creates a library loader (a header and implementation file), +which is a wrapper for dlopen or direct linking with given library. + +The loader makes it possible to have the same client code for both cases, +and also makes it easier to write code using dlopen (and also provides +a standard way to do so, and limits the ugliness just to generated files). + +For more info refer to http://crbug.com/162733 . +""" + + +import optparse +import os.path +import re +import sys + + +HEADER_TEMPLATE = """// This is generated file. Do not modify directly. +// Path to the code generator: %(generator_path)s . + +#ifndef %(unique_prefix)s +#define %(unique_prefix)s + +%(wrapped_header_include)s + +#include + +class %(class_name)s { + public: + %(class_name)s(); + ~%(class_name)s(); + + bool Load(const std::string& library_name) + __attribute__((warn_unused_result)); + + bool loaded() const { return loaded_; } + +%(member_decls)s + + private: + void CleanUp(bool unload); + +#if defined(%(unique_prefix)s_DLOPEN) + void* library_; +#endif + + bool loaded_; + + // Disallow copy constructor and assignment operator. + %(class_name)s(const %(class_name)s&); + void operator=(const %(class_name)s&); +}; + +#endif // %(unique_prefix)s +""" + + +HEADER_MEMBER_TEMPLATE = """ decltype(&::%(function_name)s) %(function_name)s; +""" + + +IMPL_TEMPLATE = """// This is generated file. Do not modify directly. +// Path to the code generator: %(generator_path)s . + +#include "%(generated_header_name)s" + +#include + +// Put these sanity checks here so that they fire at most once +// (to avoid cluttering the build output). +#if !defined(%(unique_prefix)s_DLOPEN) && !defined(%(unique_prefix)s_DT_NEEDED) +#error neither %(unique_prefix)s_DLOPEN nor %(unique_prefix)s_DT_NEEDED defined +#endif +#if defined(%(unique_prefix)s_DLOPEN) && defined(%(unique_prefix)s_DT_NEEDED) +#error both %(unique_prefix)s_DLOPEN and %(unique_prefix)s_DT_NEEDED defined +#endif + +%(class_name)s::%(class_name)s() : loaded_(false) { +} + +%(class_name)s::~%(class_name)s() { + CleanUp(loaded_); +} + +bool %(class_name)s::Load(const std::string& library_name) { + if (loaded_) + return false; + +#if defined(%(unique_prefix)s_DLOPEN) + library_ = dlopen(library_name.c_str(), RTLD_LAZY); + if (!library_) + return false; +#endif + +%(member_init)s + + loaded_ = true; + return true; +} + +void %(class_name)s::CleanUp(bool unload) { +#if defined(%(unique_prefix)s_DLOPEN) + if (unload) { + dlclose(library_); + library_ = NULL; + } +#endif + loaded_ = false; +%(member_cleanup)s +} +""" + +IMPL_MEMBER_INIT_TEMPLATE = """ +#if defined(%(unique_prefix)s_DLOPEN) + %(function_name)s = + reinterpret_cast%(function_name)s)>( + dlsym(library_, "%(function_name)s")); +#endif +#if defined(%(unique_prefix)s_DT_NEEDED) + %(function_name)s = &::%(function_name)s; +#endif + if (!%(function_name)s) { + CleanUp(true); + return false; + } +""" + +IMPL_MEMBER_CLEANUP_TEMPLATE = """ %(function_name)s = NULL; +""" + +def main(): + parser = optparse.OptionParser() + parser.add_option('--name') + parser.add_option('--output-cc') + parser.add_option('--output-h') + parser.add_option('--header') + + parser.add_option('--bundled-header') + parser.add_option('--use-extern-c', action='store_true', default=False) + parser.add_option('--link-directly', type=int, default=0) + + options, args = parser.parse_args() + + if not options.name: + parser.error('Missing --name parameter') + if not options.output_cc: + parser.error('Missing --output-cc parameter') + if not options.output_h: + parser.error('Missing --output-h parameter') + if not options.header: + parser.error('Missing --header paramater') + if not args: + parser.error('No function names specified') + + # Make sure we are always dealing with paths relative to source tree root + # to avoid issues caused by different relative path roots. + source_tree_root = os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..')) + options.output_cc = os.path.relpath(options.output_cc, source_tree_root) + options.output_h = os.path.relpath(options.output_h, source_tree_root) + + # Create a unique prefix, e.g. for header guards. + # Stick a known string at the beginning to ensure this doesn't begin + # with an underscore, which is reserved for the C++ implementation. + unique_prefix = ('LIBRARY_LOADER_' + + re.sub(r'[\W]', '_', options.output_h).upper()) + + member_decls = [] + member_init = [] + member_cleanup = [] + for fn in args: + member_decls.append(HEADER_MEMBER_TEMPLATE % { + 'function_name': fn, + 'unique_prefix': unique_prefix + }) + member_init.append(IMPL_MEMBER_INIT_TEMPLATE % { + 'function_name': fn, + 'unique_prefix': unique_prefix + }) + member_cleanup.append(IMPL_MEMBER_CLEANUP_TEMPLATE % { + 'function_name': fn, + 'unique_prefix': unique_prefix + }) + + header = options.header + if options.link_directly == 0 and options.bundled_header: + header = options.bundled_header + wrapped_header_include = '#include %s\n' % header + + # Some libraries (e.g. libpci) have headers that cannot be included + # without extern "C", otherwise they cause the link to fail. + # TODO(phajdan.jr): This is a workaround for broken headers. Remove it. + if options.use_extern_c: + wrapped_header_include = 'extern "C" {\n%s\n}\n' % wrapped_header_include + + # It seems cleaner just to have a single #define here and #ifdefs in bunch + # of places, rather than having a different set of templates, duplicating + # or complicating more code. + if options.link_directly == 0: + wrapped_header_include += '#define %s_DLOPEN\n' % unique_prefix + elif options.link_directly == 1: + wrapped_header_include += '#define %s_DT_NEEDED\n' % unique_prefix + else: + parser.error('Invalid value for --link-directly. Should be 0 or 1.') + + # Make it easier for people to find the code generator just in case. + # Doing it this way is more maintainable, because it's going to work + # even if file gets moved without updating the contents. + generator_path = os.path.relpath(__file__, source_tree_root) + + header_contents = HEADER_TEMPLATE % { + 'generator_path': generator_path, + 'unique_prefix': unique_prefix, + 'wrapped_header_include': wrapped_header_include, + 'class_name': options.name, + 'member_decls': ''.join(member_decls), + } + + impl_contents = IMPL_TEMPLATE % { + 'generator_path': generator_path, + 'unique_prefix': unique_prefix, + 'generated_header_name': options.output_h, + 'class_name': options.name, + 'member_init': ''.join(member_init), + 'member_cleanup': ''.join(member_cleanup), + } + + header_file = open(os.path.join(source_tree_root, options.output_h), 'w') + try: + header_file.write(header_contents) + finally: + header_file.close() + + impl_file = open(os.path.join(source_tree_root, options.output_cc), 'w') + try: + impl_file.write(impl_contents) + finally: + impl_file.close() + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/git/README b/engine/src/flutter/tools/git/README new file mode 100644 index 0000000000..7f8e363dee --- /dev/null +++ b/engine/src/flutter/tools/git/README @@ -0,0 +1,16 @@ +This directory contains some helpful Git tools. + +post-checkout and post-merge +============================ +These hooks warn you about DEPS modifications so you will remember +to run "gclient sync". + +To install these Git hooks, create symlinks like so: + ln -s $(pwd)/post-checkout $(git rev-parse --git-dir)/hooks + ln -s $(pwd)/post-merge $(git rev-parse --git-dir)/hooks + + +git-graph +========= +Create a graph of the recent history of occurences of a grep +expression in the project. diff --git a/engine/src/flutter/tools/git/for-all-touched-files.py b/engine/src/flutter/tools/git/for-all-touched-files.py new file mode 100755 index 0000000000..633fd078cd --- /dev/null +++ b/engine/src/flutter/tools/git/for-all-touched-files.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# Copyright (c) 2011 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. + +""" + Invokes the specified (quoted) command for all files modified + between the current git branch and the specified branch or commit. + + The special token [[FILENAME]] (or whatever you choose using the -t + flag) is replaced with each of the filenames of new or modified files. + + Deleted files are not included. Neither are untracked files. + +Synopsis: + %prog [-b BRANCH] [-d] [-x EXTENSIONS|-c|-g] [-t TOKEN] QUOTED_COMMAND + +Examples: + %prog -x gyp,gypi "tools/format_xml.py [[FILENAME]]" + %prog -c "tools/sort-headers.py [[FILENAME]]" + %prog -g "tools/sort_sources.py [[FILENAME]]" + %prog -t "~~BINGO~~" "echo I modified ~~BINGO~~" +""" + +import optparse +import os +import subprocess +import sys + + +# List of C++-like source file extensions. +_CPP_EXTENSIONS = ('h', 'hh', 'hpp', 'c', 'cc', 'cpp', 'cxx', 'mm',) +# List of build file extensions. +_BUILD_EXTENSIONS = ('gyp', 'gypi', 'gn',) + + +def GitShell(args, ignore_return=False): + """A shell invocation suitable for communicating with git. Returns + output as list of lines, raises exception on error. + """ + job = subprocess.Popen(args, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + (out, err) = job.communicate() + if job.returncode != 0 and not ignore_return: + print out + raise Exception("Error %d running command %s" % ( + job.returncode, args)) + return out.split('\n') + + +def FilenamesFromGit(branch_name, extensions): + """Provides a list of all new and modified files listed by [git diff + branch_name] where branch_name can be blank to get a diff of the + workspace. + + Excludes deleted files. + + If extensions is not an empty list, include only files with one of + the extensions on the list. + """ + lines = GitShell('git diff --stat=600,500 %s' % branch_name) + filenames = [] + for line in lines: + line = line.lstrip() + # Avoid summary line, and files that have been deleted (no plus). + if line.find('|') != -1 and line.find('+') != -1: + filename = line.split()[0] + if filename: + filename = filename.rstrip() + ext = filename.rsplit('.')[-1] + if not extensions or ext in extensions: + filenames.append(filename) + return filenames + + +def ForAllTouchedFiles(branch_name, extensions, token, command): + """For each new or modified file output by [git diff branch_name], + run command with token replaced with the filename. If extensions is + not empty, do this only for files with one of the extensions in that + list. + """ + filenames = FilenamesFromGit(branch_name, extensions) + for filename in filenames: + os.system(command.replace(token, filename)) + + +def main(): + parser = optparse.OptionParser(usage=__doc__) + parser.add_option('-x', '--extensions', default='', dest='extensions', + help='Limits to files with given extensions ' + '(comma-separated).') + parser.add_option('-c', '--cpp', default=False, action='store_true', + dest='cpp_only', + help='Runs your command only on C++-like source files.') + # -g stands for GYP and GN. + parser.add_option('-g', '--build', default=False, action='store_true', + dest='build_only', + help='Runs your command only on build files.') + parser.add_option('-t', '--token', default='[[FILENAME]]', dest='token', + help='Sets the token to be replaced for each file ' + 'in your command (default [[FILENAME]]).') + parser.add_option('-b', '--branch', default='origin/master', dest='branch', + help='Sets what to diff to (default origin/master). Set ' + 'to empty to diff workspace against HEAD.') + opts, args = parser.parse_args() + + if not args: + parser.print_help() + sys.exit(1) + + if opts.cpp_only and opts.build_only: + parser.error("--cpp and --build are mutually exclusive") + + extensions = opts.extensions + if opts.cpp_only: + extensions = _CPP_EXTENSIONS + if opts.build_only: + extensions = _BUILD_EXTENSIONS + + ForAllTouchedFiles(opts.branch, extensions, opts.token, args[0]) + + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/tools/git/git-diff-ide.py b/engine/src/flutter/tools/git/git-diff-ide.py new file mode 100755 index 0000000000..405d270eba --- /dev/null +++ b/engine/src/flutter/tools/git/git-diff-ide.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +""" + Invokes git diff [args...] and inserts file:line in front of each line of diff + output where possible. + + This is useful from an IDE that allows you to double-click lines that begin + with file:line to open and jump to that point in the file. + +Synopsis: + %prog [git diff args...] + +Examples: + %prog + %prog HEAD +""" + +import subprocess +import sys + + +def GitShell(args, ignore_return=False): + """A shell invocation suitable for communicating with git. Returns + output as list of lines, raises exception on error. + """ + job = subprocess.Popen(args, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + (out, err) = job.communicate() + if job.returncode != 0 and not ignore_return: + print out + raise Exception("Error %d running command %s" % ( + job.returncode, args)) + return out.split('\n') + + +def PrintGitDiff(extra_args): + """Outputs git diff extra_args with file:line inserted into relevant lines.""" + current_file = ''; + line_num = 0; + lines = GitShell('git diff %s' % ' '.join(extra_args)) + for line in lines: + # Pass-through lines: + # diff --git a/file.c b/file.c + # index 0e38c2d..8cd69ae 100644 + # --- a/file.c + if (line.startswith('diff ') or + line.startswith('index ') or + line.startswith('--- ')): + print line + continue + + # Get the filename from the +++ line: + # +++ b/file.c + if line.startswith('+++ '): + # Filename might be /dev/null or a/file or b/file. + # Skip the first two characters unless it starts with /. + current_file = line[4:] if line[4] == '/' else line[6:] + print line + continue + + # Update line number from the @@ lines: + # @@ -41,9 +41,9 @@ def MyFunc(): + # ^^ + if line.startswith('@@ '): + _, old_nr, new_nr, _ = line.split(' ', 3) + line_num = int(new_nr.split(',')[0]) + print line + continue + print current_file + ':' + repr(line_num) + ':' + line + + # Increment line number for lines that start with ' ' or '+': + # @@ -41,4 +41,4 @@ def MyFunc(): + # file.c:41: // existing code + # file.c:42: // existing code + # file.c:43:-// deleted code + # file.c:43:-// deleted code + # file.c:43:+// inserted code + # file.c:44:+// inserted code + if line.startswith(' ') or line.startswith('+'): + line_num += 1 + + +def main(): + PrintGitDiff(sys.argv[1:]) + + +if __name__ == '__main__': + main() diff --git a/engine/src/flutter/tools/git/git-utils.sh b/engine/src/flutter/tools/git/git-utils.sh new file mode 100755 index 0000000000..608d27aa26 --- /dev/null +++ b/engine/src/flutter/tools/git/git-utils.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright (c) 2012 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. + +TPUT=$(which tput 2>/dev/null) +if test -x "$TPUT" && $TPUT setaf 1 >/dev/null ; then + RED="$($TPUT setaf 1)" + NORMAL="$($TPUT op)" +else + RED= + NORMAL= +fi + +warn() { + echo "${RED}WARNING:${NORMAL} $@" +} diff --git a/engine/src/flutter/tools/git/graph.sh b/engine/src/flutter/tools/git/graph.sh new file mode 100755 index 0000000000..800a52b86c --- /dev/null +++ b/engine/src/flutter/tools/git/graph.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Copyright (c) 2010 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. + +about="Given a grep expression, creates a graph of occurrences of that +expression in the recent history of the tree. + +Prerequisites: git and GNU R (apt-get install r-base). +" + +set -e + +target="$1" + +if [ -z $target ]; then + echo "usage: $0 " + echo + echo "$about" + exit 1 +fi + +datafile=$(mktemp -t tmp.XXXXXXXXXX) +trap "rm -f $datafile" EXIT + +echo 'ago count' > $datafile +for ago in {90..0}; do + commit=$(git rev-list -1 --until="$ago days ago" origin/trunk) + git checkout -q -f $commit + count=$(git grep -E "$target" -- '*.cc' '*.h' '*.m' '*.mm' | wc -l) + echo "-$ago $count" >> $datafile + echo -n '.' +done + +R CMD BATCH <(cat <"])' + replacement: '\1chrome/browser/ui/browser/browser.h\3' + file_globs: ['*.cc', '*.h', '*.m', '*.mm'] + + Returns the list of files modified. + + Raises an exception on error. + """ + # Posix extended regular expressions do not reliably support the "\s" + # shorthand. + posix_ere_original = re.sub(r"\\s", "[[:space:]]", original) + if sys.platform == 'win32': + posix_ere_original = posix_ere_original.replace('"', '""') + out, err = subprocess.Popen( + ['git', 'grep', '-E', '--name-only', posix_ere_original, + '--'] + file_globs, + stdout=subprocess.PIPE, + shell=_USE_SHELL).communicate() + referees = out.splitlines() + + for referee in referees: + with open(referee) as f: + original_contents = f.read() + contents = re.sub(original, replacement, original_contents) + if contents == original_contents: + raise Exception('No change in file %s although matched in grep' % + referee) + with open(referee, 'wb') as f: + f.write(contents) + + return referees + + +def main(): + parser = optparse.OptionParser(usage=''' +(1) %prog REGEXP REPLACEMENT +REGEXP uses full Python regexp syntax. REPLACEMENT can use back-references. + +(2) %prog -i + should contain a list (in Python syntax) of +[REGEXP, REPLACEMENT, [GLOBS]] lists, e.g.: +[ + [r"(foo|bar)", r"\1baz", ["*.cc", "*.h"]], + ["54", "42"], +] +As shown above, [GLOBS] can be omitted for a given search-replace list, in which +case the corresponding search-replace will use the globs specified on the +command line.''') + parser.add_option('-d', action='store_true', + dest='use_default_glob', + help='Perform the change on C++ and Objective-C(++) source ' + 'and header files.') + parser.add_option('-f', action='store_true', + dest='force_unsafe_run', + help='Perform the run even if there are uncommitted local ' + 'changes.') + parser.add_option('-g', action='append', + type='string', + default=[], + metavar="", + dest='user_supplied_globs', + help='Perform the change on the specified glob. Can be ' + 'specified multiple times, in which case the globs are ' + 'unioned.') + parser.add_option('-i', "--input_file", + type='string', + action='store', + default='', + metavar="", + dest='input_filename', + help='Read arguments from rather than the command ' + 'line. NOTE: To be sure of regular expressions being ' + 'interpreted correctly, use raw strings.') + opts, args = parser.parse_args() + if opts.use_default_glob and opts.user_supplied_globs: + print '"-d" and "-g" cannot be used together' + parser.print_help() + return 1 + + from_file = opts.input_filename != "" + if (from_file and len(args) != 0) or (not from_file and len(args) != 2): + parser.print_help() + return 1 + + if not opts.force_unsafe_run: + out, err = subprocess.Popen(['git', 'status', '--porcelain'], + stdout=subprocess.PIPE, + shell=_USE_SHELL).communicate() + if out: + print 'ERROR: This tool does not print any confirmation prompts,' + print 'so you should only run it with a clean staging area and cache' + print 'so that reverting a bad find/replace is as easy as running' + print ' git checkout -- .' + print '' + print 'To override this safeguard, pass the -f flag.' + return 1 + + global_file_globs = ['*.*'] + if opts.use_default_glob: + global_file_globs = ['*.cc', '*.h', '*.m', '*.mm'] + elif opts.user_supplied_globs: + global_file_globs = opts.user_supplied_globs + + # Construct list of search-replace tasks. + search_replace_tasks = [] + if opts.input_filename == '': + original = args[0] + replacement = args[1] + search_replace_tasks.append([original, replacement, global_file_globs]) + else: + f = open(opts.input_filename) + search_replace_tasks = eval("".join(f.readlines())) + for task in search_replace_tasks: + if len(task) == 2: + task.append(global_file_globs) + f.close() + + for (original, replacement, file_globs) in search_replace_tasks: + print 'File globs: %s' % file_globs + print 'Original: %s' % original + print 'Replacement: %s' % replacement + MultiFileFindReplace(original, replacement, file_globs) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/git/move_source_file.bat b/engine/src/flutter/tools/git/move_source_file.bat new file mode 100755 index 0000000000..bc3d7978e0 --- /dev/null +++ b/engine/src/flutter/tools/git/move_source_file.bat @@ -0,0 +1,6 @@ +@echo off +setlocal +:: This is required with cygwin only. +PATH=%~dp0;%PATH% +set PYTHONDONTWRITEBYTECODE=1 +call python "%~dp0move_source_file.py" %* diff --git a/engine/src/flutter/tools/git/move_source_file.py b/engine/src/flutter/tools/git/move_source_file.py new file mode 100755 index 0000000000..18ef1ce827 --- /dev/null +++ b/engine/src/flutter/tools/git/move_source_file.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Moves C++ files to a new location, updating any include paths that point +to them, and re-ordering headers as needed. If multiple source files are +specified, the destination must be a directory. Updates include guards in +moved header files. Assumes Chromium coding style. + +Attempts to update paths used in .gyp(i) files, but does not reorder +or restructure .gyp(i) files in any way. + +Updates full-path references to files in // comments in source files. + +Must run in a git checkout, as it relies on git grep for a fast way to +find files that reference the moved file. +""" + + +import optparse +import os +import re +import subprocess +import sys + +import mffr + +if __name__ == '__main__': + # Need to add the directory containing sort-headers.py to the Python + # classpath. + sys.path.append(os.path.abspath(os.path.join(sys.path[0], '..'))) +sort_headers = __import__('sort-headers') +import sort_sources + + +HANDLED_EXTENSIONS = ['.cc', '.mm', '.h', '.hh', '.cpp'] + + +def IsHandledFile(path): + return os.path.splitext(path)[1] in HANDLED_EXTENSIONS + + +def MakeDestinationPath(from_path, to_path): + """Given the from and to paths, return a correct destination path. + + The initial destination path may either a full path or a directory. + Also does basic sanity checks. + """ + if not IsHandledFile(from_path): + raise Exception('Only intended to move individual source files ' + '(%s does not have a recognized extension).' % + from_path) + + # Remove '.', '..', etc. + to_path = os.path.normpath(to_path) + + if os.path.isdir(to_path): + to_path = os.path.join(to_path, os.path.basename(from_path)) + else: + dest_extension = os.path.splitext(to_path)[1] + if dest_extension not in HANDLED_EXTENSIONS: + raise Exception('Destination must be either a full path with ' + 'a recognized extension or a directory.') + return to_path + + +def MoveFile(from_path, to_path): + """Performs a git mv command to move a file from |from_path| to |to_path|. + """ + if not os.system('git mv %s %s' % (from_path, to_path)) == 0: + raise Exception('Fatal: Failed to run git mv command.') + + +def UpdatePostMove(from_path, to_path): + """Given a file that has moved from |from_path| to |to_path|, + updates the moved file's include guard to match the new path and + updates all references to the file in other source files. Also tries + to update references in .gyp(i) files using a heuristic. + """ + # Include paths always use forward slashes. + from_path = from_path.replace('\\', '/') + to_path = to_path.replace('\\', '/') + + if os.path.splitext(from_path)[1] in ['.h', '.hh']: + UpdateIncludeGuard(from_path, to_path) + + # Update include/import references. + files_with_changed_includes = mffr.MultiFileFindReplace( + r'(#(include|import)\s*["<])%s([>"])' % re.escape(from_path), + r'\1%s\3' % to_path, + ['*.cc', '*.h', '*.m', '*.mm', '*.cpp']) + + # Reorder headers in files that changed. + for changed_file in files_with_changed_includes: + def AlwaysConfirm(a, b): return True + sort_headers.FixFileWithConfirmFunction(changed_file, AlwaysConfirm, True) + + # Update comments; only supports // comments, which are primarily + # used in our code. + # + # This work takes a bit of time. If this script starts feeling too + # slow, one good way to speed it up is to make the comment handling + # optional under a flag. + mffr.MultiFileFindReplace( + r'(//.*)%s' % re.escape(from_path), + r'\1%s' % to_path, + ['*.cc', '*.h', '*.m', '*.mm', '*.cpp']) + + # Update references in GYP and BUILD.gn files. + # + # GYP files are mostly located under the first level directory (ex. + # chrome/chrome_browser.gypi), but sometimes they are located in + # directories at a deeper level (ex. extensions/shell/app_shell.gypi). On + # the other hand, BUILD.gn files can be placed in any directories. + # + # Paths in a GYP or BUILD.gn file are relative to the directory where the + # file is placed. + # + # For instance, "chrome/browser/chromeos/device_uma.h" is listed as + # "browser/chromeos/device_uma.h" in "chrome/chrome_browser_chromeos.gypi", + # but it's listed as "device_uma.h" in "chrome/browser/chromeos/BUILD.gn". + # + # To handle this, the code here will visit directories from the top level + # src directory to the directory of |from_path| and try to update GYP and + # BUILD.gn files in each directory. + # + # The code only handles files moved/renamed within the same build file. If + # files are moved beyond the same build file, the affected build files + # should be fixed manually. + def SplitByFirstComponent(path): + """'foo/bar/baz' -> ('foo', 'bar/baz') + 'bar' -> ('bar', '') + '' -> ('', '') + """ + parts = re.split(r"[/\\]", path, 1) + if len(parts) == 2: + return (parts[0], parts[1]) + else: + return (parts[0], '') + + visiting_directory = '' + from_rest = from_path + to_rest = to_path + while True: + files_with_changed_sources = mffr.MultiFileFindReplace( + r'([\'"])%s([\'"])' % from_rest, + r'\1%s\2' % to_rest, + [os.path.join(visiting_directory, 'BUILD.gn'), + os.path.join(visiting_directory, '*.gyp*')]) + for changed_file in files_with_changed_sources: + sort_sources.ProcessFile(changed_file, should_confirm=False) + from_first, from_rest = SplitByFirstComponent(from_rest) + to_first, to_rest = SplitByFirstComponent(to_rest) + visiting_directory = os.path.join(visiting_directory, from_first) + if not from_rest or not to_rest: + break + + +def MakeIncludeGuardName(path_from_root): + """Returns an include guard name given a path from root.""" + guard = path_from_root.replace('/', '_') + guard = guard.replace('\\', '_') + guard = guard.replace('.', '_') + guard += '_' + return guard.upper() + + +def UpdateIncludeGuard(old_path, new_path): + """Updates the include guard in a file now residing at |new_path|, + previously residing at |old_path|, with an up-to-date include guard. + + Prints a warning if the update could not be completed successfully (e.g., + because the old include guard was not formatted correctly per Chromium style). + """ + old_guard = MakeIncludeGuardName(old_path) + new_guard = MakeIncludeGuardName(new_path) + + with open(new_path) as f: + contents = f.read() + + new_contents = contents.replace(old_guard, new_guard) + # The file should now have three instances of the new guard: two at the top + # of the file plus one at the bottom for the comment on the #endif. + if new_contents.count(new_guard) != 3: + print ('WARNING: Could not successfully update include guard; perhaps ' + 'old guard is not per style guide? You will have to update the ' + 'include guard manually. (%s)' % new_path) + + with open(new_path, 'w') as f: + f.write(new_contents) + +def main(): + if not os.path.isdir('.git'): + print 'Fatal: You must run from the root of a git checkout.' + return 1 + + parser = optparse.OptionParser(usage='%prog FROM_PATH... TO_PATH') + parser.add_option('--already_moved', action='store_true', + dest='already_moved', + help='Causes the script to skip moving the file.') + parser.add_option('--no_error_for_non_source_file', action='store_false', + default='True', + dest='error_for_non_source_file', + help='Causes the script to simply print a warning on ' + 'encountering a non-source file rather than raising an ' + 'error.') + opts, args = parser.parse_args() + + if len(args) < 2: + parser.print_help() + return 1 + + from_paths = args[:len(args)-1] + orig_to_path = args[-1] + + if len(from_paths) > 1 and not os.path.isdir(orig_to_path): + print 'Target %s is not a directory.' % orig_to_path + print + parser.print_help() + return 1 + + for from_path in from_paths: + if not opts.error_for_non_source_file and not IsHandledFile(from_path): + print '%s does not appear to be a source file, skipping' % (from_path) + continue + to_path = MakeDestinationPath(from_path, orig_to_path) + if not opts.already_moved: + MoveFile(from_path, to_path) + UpdatePostMove(from_path, to_path) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/git/post-checkout b/engine/src/flutter/tools/git/post-checkout new file mode 100755 index 0000000000..452eb48eb4 --- /dev/null +++ b/engine/src/flutter/tools/git/post-checkout @@ -0,0 +1,22 @@ +#!/bin/bash +# Copyright (c) 2010 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. + +script=$(readlink $0) +source $(dirname ${script:-$0})/git-utils.sh + +old_ref=$1 # Previous HEAD. +new_ref=$2 # Current HEAD. +branch_switch=$3 # Whether we switched branches. + +if [ $old_ref == $new_ref ]; then + if ! git diff-index --quiet HEAD $(git rev-parse --show-cdup)DEPS; then + warn "DEPS has local modifications; do you need to re-run gclient sync?" + fi +else + if git diff-tree $old_ref $new_ref | grep -qs $'\tDEPS$'; then + warn "DEPS has changed; you probably need to re-run gclient sync." + fi +fi + diff --git a/engine/src/flutter/tools/git/post-merge b/engine/src/flutter/tools/git/post-merge new file mode 100755 index 0000000000..8b774ce659 --- /dev/null +++ b/engine/src/flutter/tools/git/post-merge @@ -0,0 +1,12 @@ +#!/bin/bash +# Copyright (c) 2010 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. + +script=$(readlink $0) +source $(dirname ${script:-$0})/git-utils.sh + +if git diff-tree ORIG_HEAD HEAD | grep -qs $'\tDEPS$'; then + warn "DEPS has changed; you probably need to re-run gclient sync." +fi + diff --git a/engine/src/flutter/tools/git/update-copyrights.sh b/engine/src/flutter/tools/git/update-copyrights.sh new file mode 100755 index 0000000000..ac69bd53e6 --- /dev/null +++ b/engine/src/flutter/tools/git/update-copyrights.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Copyright (c) 2012 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. + +echo 'Updating copyrights is no longer necessary.' +echo 'See https://groups.google.com/a/chromium.org/d/msg/chromium-dev/8p4JKV76kig/OiFYFjuZ6nAJ' diff --git a/engine/src/flutter/tools/go/VERSION_LINUX b/engine/src/flutter/tools/go/VERSION_LINUX new file mode 100644 index 0000000000..16697bada1 --- /dev/null +++ b/engine/src/flutter/tools/go/VERSION_LINUX @@ -0,0 +1 @@ +390daacbfac646491c8ac7469195105ac0ce2c46 diff --git a/engine/src/flutter/tools/go/VERSION_MACOSX b/engine/src/flutter/tools/go/VERSION_MACOSX new file mode 100644 index 0000000000..5c0a98cd20 --- /dev/null +++ b/engine/src/flutter/tools/go/VERSION_MACOSX @@ -0,0 +1 @@ +72392dc25aec8d55aa7755c89a281270cdf4ef6d diff --git a/engine/src/flutter/tools/go/download.py b/engine/src/flutter/tools/go/download.py new file mode 100755 index 0000000000..930623b19b --- /dev/null +++ b/engine/src/flutter/tools/go/download.py @@ -0,0 +1,91 @@ +#!/usr/bin/python +# Copyright 2015 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. + +""" +Downloads Go binaries from Google Cloud Storage and extracts them to +INSTALL_DIR, updating INSTALL_DIR/VERSION stamp file with current version. +Does nothing if INSTALL_DIR/VERSION is already up to date. +""" + +import os +import shutil +import subprocess +import sys +import tarfile + +# Path constants. (All of these should be absolute paths.) +THIS_DIR = os.path.abspath(os.path.dirname(__file__)) +MOJO_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..')) +# Should be the same as in upload.py. +INSTALL_DIR = os.path.join(MOJO_DIR, 'third_party', 'go', 'tool') + +sys.path.insert(0, os.path.join(MOJO_DIR, 'tools')) +import find_depot_tools + +DEPOT_PATH = find_depot_tools.add_depot_tools_to_path() +GSUTIL_PATH = os.path.join(DEPOT_PATH, 'gsutil.py') + +def RunCommand(command): + """Run command and return success (True) or failure.""" + + print 'Running %s' % (str(command)) + if subprocess.call(command, shell=False) == 0: + return True + print 'Failed.' + return False + +def VersionFileName(): + if sys.platform.startswith('linux'): + platform_suffix = 'LINUX' + elif sys.platform == 'darwin': + platform_suffix = 'MACOSX' + else: + raise Exception('unsupported platform: ' + sys.platform) + return 'VERSION_' + platform_suffix + +def GetInstalledVersion(): + version_file = os.path.join(INSTALL_DIR, VersionFileName()) + if not os.path.exists(version_file): + return None + with open(version_file) as f: + return f.read().strip() + +def InstallGoBinaries(version): + """Downloads zipped go binaries from Google Cloud Storage and extracts them, + stamping current version.""" + + # Remove current installation. + if os.path.exists(INSTALL_DIR): + shutil.rmtree(INSTALL_DIR) + os.mkdir(INSTALL_DIR) + # Download go tool binaries from GCS. + archive_path = os.path.join(INSTALL_DIR, 'go.tar.gz') + download_cmd = ['python', GSUTIL_PATH, '-b', 'cp', + 'gs://mojo/go/tool/%s.tar.gz' % version, + archive_path] + if not RunCommand(download_cmd): + print ('WARNING: Failed to download Go tool binaries.') + return + + print "Extracting Go binaries." + with tarfile.open(archive_path) as arch: + arch.extractall(INSTALL_DIR) + os.remove(archive_path) + # Write version as the last step. + with open(os.path.join(INSTALL_DIR, VersionFileName()), 'w+') as f: + f.write('%s\n' % version) + +def main(): + # Read latest version. + version = '' + with open(os.path.join(THIS_DIR, VersionFileName())) as f: + version = f.read().strip() + # Return if installed binaries are up to date. + if version == GetInstalledVersion(): + return + InstallGoBinaries(version) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/go/upload.py b/engine/src/flutter/tools/go/upload.py new file mode 100755 index 0000000000..893c79af2c --- /dev/null +++ b/engine/src/flutter/tools/go/upload.py @@ -0,0 +1,177 @@ +#!/usr/bin/python +# Copyright 2015 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. + +""" +This script takes linux Go binaries, builds android binaries with NDK tool chain +configured with NDK_PLATFORM and NDK_TOOLCHAIN parameters, zips the stuff and +uploads it to Google Cloud Storage at gs://mojo/go/tool. It also produces +the VERSION file with the sha1 code of the uploaded archive. + +This script operates in the INSTALL_DIR directory, so it automatically updates +your current installation of the go binaries on success. On failure it +invalidates your current installation; to fix it, run download.py. + +In order to use it, you need: +- depot_tools in your path +- installed android build deps +- WRITE access to GCS + +To update go tool binaries you need to +1) run 'gsutil.py config' to update initialize gsutil's credentials +2) run this script: python upload.py path/to/go/binaries.tar.gz +3) push new version of file 'VERSION' + +This script doesn't check if current version is already up to date, as the +produced tar.gz archive is slightly different every time since it includes +timestamps. +""" + +import hashlib +import os +import shutil +import subprocess +import sys +import tarfile +import tempfile + +NDK_PLATFORM = 'android-14' +NDK_TOOLCHAIN = 'arm-linux-androideabi-4.9' + +# Path constants. (All of these should be absolute paths.) +THIS_DIR = os.path.abspath(os.path.dirname(__file__)) +MOJO_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..')) +# Should be the same as in download.py. +INSTALL_DIR = os.path.join(MOJO_DIR, 'third_party', 'go', 'tool') + +sys.path.insert(0, os.path.join(MOJO_DIR, 'tools')) +import find_depot_tools + +DEPOT_PATH = find_depot_tools.add_depot_tools_to_path() +GSUTIL_PATH = os.path.join(DEPOT_PATH, 'gsutil.py') + +def RunCommand(command, env=None): + """Run command and return success (True) or failure.""" + + print 'Running %s' % (str(command)) + if subprocess.call(command, shell=False, env=env) == 0: + return True + print 'Failed.' + return False + +def VersionFileName(): + if sys.platform.startswith('linux'): + platform_suffix = 'LINUX' + elif sys.platform == 'darwin': + platform_suffix = 'MACOSX' + else: + raise Exception('unsupported platform: ' + sys.platform) + return 'VERSION_' + platform_suffix + +def HostArchName(): + if sys.platform.startswith('linux'): + arch = 'linux_amd64' + elif sys.platform == 'darwin': + arch = 'darwin_amd64' + else: + raise Exception('unsupported platform: ' + sys.platform) + return arch + +def ExtractBinaries(archive_path): + """Extracts go binaries from the given tar file to INSTALL_DIR.""" + + if os.path.exists(INSTALL_DIR): + shutil.rmtree(INSTALL_DIR) + os.mkdir(INSTALL_DIR) + archive_path = os.path.abspath(archive_path) + with tarfile.open(archive_path) as arch: + arch.extractall(INSTALL_DIR) + os.rename(os.path.join(INSTALL_DIR, 'go'), + os.path.join(INSTALL_DIR, HostArchName())) + +def BuildGoAndroid(): + go_host = os.path.join(INSTALL_DIR, HostArchName()) + go_android = os.path.join(INSTALL_DIR, 'android_arm') + # Copy go sources and remove binaries to keep only that we generate. + shutil.copytree(go_host, go_android) + shutil.rmtree(os.path.join(go_android, 'bin')) + shutil.rmtree(os.path.join(go_android, 'pkg')) + # Prepare the Android NDK tool chain. + os.chdir(os.path.join(MOJO_DIR, 'third_party', 'android_tools', 'ndk')) + ndk_out_dir = tempfile.mkdtemp(prefix='android_ndk') + script_path = os.path.join('build', 'tools', 'make-standalone-toolchain.sh') + make_toolchain_cmd = ['bash', script_path, '--platform=%s' % NDK_PLATFORM, + '--toolchain=%s' % NDK_TOOLCHAIN, + '--install-dir=%s' % ndk_out_dir] + if not RunCommand(make_toolchain_cmd): + print "Faild to build the Android NDK tool chain." + sys.exit(1) + # Configure environment variables. + cc = os.path.join(ndk_out_dir, 'bin', 'arm-linux-androideabi-gcc') + env = os.environ.copy() + env["GOROOT"] = go_host + env["GOROOT_BOOTSTRAP"] = go_host + env["CC_FOR_TARGET"] = '%s' % cc + env["CGO_ENABLED"] = '1' + env["GOOS"] = 'android' + env["GOARCH"] = 'arm' + env["GOARM"] = '7' + # Build go tool. + os.chdir(os.path.join(go_android, 'src')) + make_command = ['bash', 'make.bash'] + if not RunCommand(make_command, env=env): + print "Failed to make go tool for android." + sys.exit(1) + shutil.rmtree(ndk_out_dir) + +def Compress(): + """Compresses the go tool into tar.gz and generates sha1 code, renames the + archive to sha1.tar.gz and returns the sha1 code.""" + + print "Compressing go tool, this may take several minutes." + os.chdir(INSTALL_DIR) + with tarfile.open(os.path.join('a.tar.gz'), 'w|gz') as arch: + arch.add('android_arm') + arch.add(HostArchName()) + + sha1 = '' + with open(os.path.join(INSTALL_DIR, 'a.tar.gz')) as f: + sha1 = hashlib.sha1(f.read()).hexdigest() + os.rename(os.path.join(INSTALL_DIR, 'a.tar.gz'), + os.path.join(INSTALL_DIR, '%s.tar.gz' % sha1)) + return sha1 + +def Upload(sha1): + """Uploads INSTALL_DIR/sha1.tar.gz to Google Cloud Storage under + gs://mojo/go/tool and writes sha1 to THIS_DIR/VERSION.""" + + file_name = '%s.tar.gz' % sha1 + upload_cmd = ['python', GSUTIL_PATH, 'cp', + '-n', # Do not upload if the file already exists. + os.path.join(INSTALL_DIR, file_name), + 'gs://mojo/go/tool/%s' % file_name] + + print "Uploading go tool to GCS." + if not RunCommand(upload_cmd): + print "Failed to upload go tool to GCS." + sys.exit(1) + os.remove(os.path.join(INSTALL_DIR, file_name)) + # Write versions as the last step. + stamp_file = os.path.join(THIS_DIR, VersionFileName()) + with open(stamp_file, 'w+') as stamp: + stamp.write('%s\n' % sha1) + + stamp_file = os.path.join(INSTALL_DIR, VersionFileName()) + with open(stamp_file, 'w+') as stamp: + stamp.write('%s\n' % sha1) + +def main(): + ExtractBinaries(sys.argv[1]) + BuildGoAndroid() + sha1 = Compress() + Upload(sha1) + print "Done." + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/gritsettings/resource_ids b/engine/src/flutter/tools/gritsettings/resource_ids new file mode 100644 index 0000000000..79f7601eec --- /dev/null +++ b/engine/src/flutter/tools/gritsettings/resource_ids @@ -0,0 +1,274 @@ +# Copyright (c) 2012 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. +# +# This file is used to assign starting resource ids for resources and strings +# used by Chromium. This is done to ensure that resource ids are unique +# across all the grd files. If you are adding a new grd file, please add +# a new entry to this file. +# +# The first entry in the file, SRCDIR, is special: It is a relative path from +# this file to the base of your checkout. +# +# The range of ID values, which is used by pak files, is from 0 to 2^16 - 1. +{ + "SRCDIR": "../..", + + "chrome/browser/browser_resources.grd": { + "includes": [400], + "structures": [750], + }, + "chrome/browser/resources/component_extension_resources.grd": { + "includes": [1000], + "structures": [1450], + }, + "chrome/browser/resources/net_internals_resources.grd": { + "includes": [1500], + }, + "ui/webui/resources/webui_resources.grd": { + "includes": [2000], + "structures": [2200], + }, + "chrome/common/common_resources.grd": { + "includes": [3000], + }, + "chrome/renderer/resources/renderer_resources.grd": { + "includes": [3500], + "structures": [3700], + }, + "net/base/net_resources.grd": { + "includes": [4000], + }, + "ui/resources/ui_unscaled_resources.grd": { + "includes": [4500], + }, + "content/app/resources/content_resources.grd": { + "structures": [4700], + }, + "third_party/WebKit/public/blink_image_resources.grd": { + "structures": [4750], + }, + "ui/resources/ui_resources.grd": { + "structures": [5500], + }, + "ash/resources/ash_resources.grd": { + "includes": [6100], + "structures": [6150], + }, + "athena/resources/athena_resources.grd": { + "structures": [6400], + }, + "athena/strings/athena_strings.grd": { + "messages": [6500], + }, + "chrome/app/theme/theme_resources.grd": { + "structures": [7000], + }, + "chrome/app/theme/chrome_unscaled_resources.grd": { + "includes": [8000], + }, + "ui/strings/app_locale_settings.grd": { + "messages": [9000], + }, + "chrome/app/resources/locale_settings.grd": { + "includes": [9500], + "messages": [10000], + }, + # These each start with the same resource id because we only use one + # file for each build (chromiumos, google_chromeos, linux, mac, or win). + "chrome/app/resources/locale_settings_chromiumos.grd": { + "messages": [10500], + }, + "chrome/app/resources/locale_settings_google_chromeos.grd": { + "messages": [10500], + }, + "chrome/app/resources/locale_settings_linux.grd": { + "messages": [10500], + }, + "chrome/app/resources/locale_settings_mac.grd": { + "messages": [10500], + }, + "chrome/app/resources/locale_settings_win.grd": { + "messages": [10500], + }, + "ui/strings/ui_strings.grd": { + "messages": [11000], + }, + # Chromium strings and Google Chrome strings must start at the same id. + # We only use one file depending on whether we're building Chromium or + # Google Chrome. + "chrome/app/chromium_strings.grd": { + "messages": [11500], + }, + "chrome/app/google_chrome_strings.grd": { + "messages": [11500], + }, + # Leave lots of space for generated_resources since it has most of our + # strings. + "chrome/app/generated_resources.grd": { + "structures": [12000], + "messages": [12500], + }, + "content/app/strings/content_strings.grd": { + "messages": [19000], + }, + "components/policy/resources/policy_templates.grd": { + "structures": [20500], + "messages": [20510], + }, + "chrome/browser/resources/sync_internals_resources.grd": { + "includes": [21500], + }, + "chrome/browser/resources/signin_internals_resources.grd": { + "includes": [21750], + }, + "chrome/browser/resources/invalidations_resources.grd": { + "includes": [21950], + }, + # This file is generated during the build. + "<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd": { + "includes": [22000], + }, + "devtools_resources.grd": { + "includes": [22000], + }, + "chrome/browser/resources/options_resources.grd": { + "includes": [23000], + "structures": [23200], + }, + "chrome/browser/resources/options_test_resources.grd": { + "includes": [23400], + "structures": [23450], + }, + "cloud_print/virtual_driver/win/install/virtual_driver_setup_resources.grd": { + "messages": [23500], + "includes": [23550], + }, + "cloud_print/service/win/service_resources.grd": { + "messages": [23600], + "includes": [23700], + "structures": [23750], + }, + "cloud_print/gcp20/prototype/gcp20_device.grd": { + "messages": [23800], + "includes": [23830], + "structures": [23860], + }, + "chrome/browser/resources/quota_internals_resources.grd": { + "includes": [24000], + }, + "content/content_resources.grd": { + "includes": [25000], + }, + "content/shell/shell_resources.grd": { + "includes": [25500], + }, + # This file is generated during the build. + "<(SHARED_INTERMEDIATE_DIR)/content/browser/tracing/tracing_resources.grd": { + "includes": [25750], + }, + # iOS resources overlap with ash, chromeos and extensions_api, as these are + # not used on iOS. + "ios/chrome/app/strings/ios_strings_resources.grd": { + "messages": [26000], + }, + "ios/chrome/app/theme/ios_theme_resources.grd": { + "structures": [27000], + }, + "ash/ash_strings.grd": { + "messages": [26000], + }, + "ui/chromeos/resources/ui_chromeos_resources.grd": { + "structures": [26200], + }, + "ui/chromeos/ui_chromeos_strings.grd": { + "messages": [26300], + }, + "chrome/common/extensions_api_resources.grd": { + "includes": [26400], + }, + "extensions/extensions_resources.grd": { + "includes": [26600], + }, + "extensions/browser/resources/extensions_browser_resources.grd": { + "structures": [26800], + }, + "extensions/renderer/resources/extensions_renderer_resources.grd": { + "includes": [26850], + "structures": [26950], + }, + "extensions/extensions_strings.grd": { + "messages": [27000], + }, + "extensions/shell/app_shell_resources.grd": { + "includes": [27400], + }, + "chrome/browser/resources/memory_internals_resources.grd": { + "includes": [27500], + }, + "chrome/browser/resources/password_manager_internals_resources.grd": { + "includes": [27800], + }, + "device/bluetooth/bluetooth_strings.grd": { + "messages": [28000], + }, + "ui/keyboard/keyboard_resources.grd": { + "includes": [28050], + }, + "ui/file_manager/file_manager_resources.grd": { + "includes": [28110], + }, + "components/chrome_apps/chrome_apps_resources.grd": { + "includes": [28290], + }, + "ui/login/login_resources.grd": { + "includes": [28310], + }, + "ui/oobe/oobe_resources.grd": { + "includes": [28320], + }, + "chrome/browser/resources/translate_internals_resources.grd": { + "includes": [28510], + }, + "chrome/browser/resources/sync_file_system_internals_resources.grd": { + "includes": [29010], + }, + "chrome/app/address_input_strings.grd": { + "messages": [29110], + }, + "remoting/resources/remoting_strings.grd": { + "messages": [29560], + }, + "components/components_strings.grd": { + "messages": [30010], + }, + "components/resources/components_resources.grd": { + "includes": [30260], + }, + "components/resources/components_scaled_resources.grd": { + "structures": [30310], + }, + "components/resources/enhanced_bookmarks/enhanced_bookmarks_resources.grd": { + "includes": [30340], + }, + "third_party/WebKit/public/blink_resources.grd": { + "includes": [30350], + "structures": [30650], + }, + "chrome/browser/devtools/device/webrtc/resources.grd": { + "includes": [30800], + }, + "chrome/browser/resources/settings/settings_resources.grd": { + "structures": [30900], + }, + + # These files don't need to reserve resource ids, but are listed here so that + # translation scripts know of their existence. + "android_webview/java/strings/android_webview_strings.grd": {}, + "chrome/android/java/strings/android_chrome_strings.grd": {}, + "content/public/android/java/strings/android_content_strings.grd": {}, + "ui/accessibility/extensions/strings/accessibility_extensions_strings.grd": {}, + "ui/android/java/strings/android_ui_strings.grd": {}, + + # Resource ids starting at 31000 are reserved for projects built on Chromium. +} diff --git a/engine/src/flutter/tools/gyp/pylib/gyp/mac_tool.py b/engine/src/flutter/tools/gyp/pylib/gyp/mac_tool.py new file mode 100755 index 0000000000..eeeaceb0c7 --- /dev/null +++ b/engine/src/flutter/tools/gyp/pylib/gyp/mac_tool.py @@ -0,0 +1,610 @@ +#!/usr/bin/env python +# Copyright (c) 2012 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Utility functions to perform Xcode-style build steps. + +These functions are executed via gyp-mac-tool when using the Makefile generator. +""" + +import fcntl +import fnmatch +import glob +import json +import os +import plistlib +import re +import shutil +import string +import subprocess +import sys +import tempfile + + +def main(args): + executor = MacTool() + exit_code = executor.Dispatch(args) + if exit_code is not None: + sys.exit(exit_code) + + +class MacTool(object): + """This class performs all the Mac tooling steps. The methods can either be + executed directly, or dispatched from an argument list.""" + + def Dispatch(self, args): + """Dispatches a string command to a method.""" + if len(args) < 1: + raise Exception("Not enough arguments") + + method = "Exec%s" % self._CommandifyName(args[0]) + return getattr(self, method)(*args[1:]) + + def _CommandifyName(self, name_string): + """Transforms a tool name like copy-info-plist to CopyInfoPlist""" + return name_string.title().replace('-', '') + + def ExecCopyBundleResource(self, source, dest, convert_to_binary): + """Copies a resource file to the bundle/Resources directory, performing any + necessary compilation on each resource.""" + extension = os.path.splitext(source)[1].lower() + if os.path.isdir(source): + # Copy tree. + # TODO(thakis): This copies file attributes like mtime, while the + # single-file branch below doesn't. This should probably be changed to + # be consistent with the single-file branch. + if os.path.exists(dest): + shutil.rmtree(dest) + shutil.copytree(source, dest) + elif extension == '.xib': + return self._CopyXIBFile(source, dest) + elif extension == '.storyboard': + return self._CopyXIBFile(source, dest) + elif extension == '.strings': + self._CopyStringsFile(source, dest, convert_to_binary) + else: + shutil.copy(source, dest) + + def _CopyXIBFile(self, source, dest): + """Compiles a XIB file with ibtool into a binary plist in the bundle.""" + + # ibtool sometimes crashes with relative paths. See crbug.com/314728. + base = os.path.dirname(os.path.realpath(__file__)) + if os.path.relpath(source): + source = os.path.join(base, source) + if os.path.relpath(dest): + dest = os.path.join(base, dest) + + args = ['xcrun', 'ibtool', '--errors', '--warnings', '--notices', + '--output-format', 'human-readable-text', '--compile', dest, source] + ibtool_section_re = re.compile(r'/\*.*\*/') + ibtool_re = re.compile(r'.*note:.*is clipping its content') + ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) + current_section_header = None + for line in ibtoolout.stdout: + if ibtool_section_re.match(line): + current_section_header = line + elif not ibtool_re.match(line): + if current_section_header: + sys.stdout.write(current_section_header) + current_section_header = None + sys.stdout.write(line) + return ibtoolout.returncode + + def _ConvertToBinary(self, dest): + subprocess.check_call([ + 'xcrun', 'plutil', '-convert', 'binary1', '-o', dest, dest]) + + def _CopyStringsFile(self, source, dest, convert_to_binary): + """Copies a .strings file using iconv to reconvert the input into UTF-16.""" + input_code = self._DetectInputEncoding(source) or "UTF-8" + + # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call + # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints + # CFPropertyListCreateFromXMLData(): Old-style plist parser: missing + # semicolon in dictionary. + # on invalid files. Do the same kind of validation. + import CoreFoundation + s = open(source, 'rb').read() + d = CoreFoundation.CFDataCreate(None, s, len(s)) + _, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None) + if error: + return + + fp = open(dest, 'wb') + fp.write(s.decode(input_code).encode('UTF-16')) + fp.close() + + if convert_to_binary == 'True': + self._ConvertToBinary(dest) + + def _DetectInputEncoding(self, file_name): + """Reads the first few bytes from file_name and tries to guess the text + encoding. Returns None as a guess if it can't detect it.""" + fp = open(file_name, 'rb') + try: + header = fp.read(3) + except e: + fp.close() + return None + fp.close() + if header.startswith("\xFE\xFF"): + return "UTF-16" + elif header.startswith("\xFF\xFE"): + return "UTF-16" + elif header.startswith("\xEF\xBB\xBF"): + return "UTF-8" + else: + return None + + def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys): + """Copies the |source| Info.plist to the destination directory |dest|.""" + # Read the source Info.plist into memory. + fd = open(source, 'r') + lines = fd.read() + fd.close() + + # Insert synthesized key/value pairs (e.g. BuildMachineOSBuild). + plist = plistlib.readPlistFromString(lines) + if keys: + plist = dict(plist.items() + json.loads(keys[0]).items()) + lines = plistlib.writePlistToString(plist) + + # Go through all the environment variables and replace them as variables in + # the file. + IDENT_RE = re.compile(r'[/\s]') + for key in os.environ: + if key.startswith('_'): + continue + evar = '${%s}' % key + evalue = os.environ[key] + lines = string.replace(lines, evar, evalue) + + # Xcode supports various suffices on environment variables, which are + # all undocumented. :rfc1034identifier is used in the standard project + # template these days, and :identifier was used earlier. They are used to + # convert non-url characters into things that look like valid urls -- + # except that the replacement character for :identifier, '_' isn't valid + # in a URL either -- oops, hence :rfc1034identifier was born. + evar = '${%s:identifier}' % key + evalue = IDENT_RE.sub('_', os.environ[key]) + lines = string.replace(lines, evar, evalue) + + evar = '${%s:rfc1034identifier}' % key + evalue = IDENT_RE.sub('-', os.environ[key]) + lines = string.replace(lines, evar, evalue) + + # Remove any keys with values that haven't been replaced. + lines = lines.split('\n') + for i in range(len(lines)): + if lines[i].strip().startswith("${"): + lines[i] = None + lines[i - 1] = None + lines = '\n'.join(filter(lambda x: x is not None, lines)) + + # Write out the file with variables replaced. + fd = open(dest, 'w') + fd.write(lines) + fd.close() + + # Now write out PkgInfo file now that the Info.plist file has been + # "compiled". + self._WritePkgInfo(dest) + + if convert_to_binary == 'True': + self._ConvertToBinary(dest) + + def _WritePkgInfo(self, info_plist): + """This writes the PkgInfo file from the data stored in Info.plist.""" + plist = plistlib.readPlist(info_plist) + if not plist: + return + + # Only create PkgInfo for executable types. + package_type = plist['CFBundlePackageType'] + if package_type != 'APPL': + return + + # The format of PkgInfo is eight characters, representing the bundle type + # and bundle signature, each four characters. If that is missing, four + # '?' characters are used instead. + signature_code = plist.get('CFBundleSignature', '????') + if len(signature_code) != 4: # Wrong length resets everything, too. + signature_code = '?' * 4 + + dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') + fp = open(dest, 'w') + fp.write('%s%s' % (package_type, signature_code)) + fp.close() + + def ExecFlock(self, lockfile, *cmd_list): + """Emulates the most basic behavior of Linux's flock(1).""" + # Rely on exception handling to report errors. + fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) + fcntl.flock(fd, fcntl.LOCK_EX) + return subprocess.call(cmd_list) + + def ExecFilterLibtool(self, *cmd_list): + """Calls libtool and filters out '/path/to/libtool: file: foo.o has no + symbols'.""" + libtool_re = re.compile(r'^.*libtool: file: .* has no symbols$') + libtool_re5 = re.compile( + r'^.*libtool: warning for library: ' + + r'.* the table of contents is empty ' + + r'\(no object file members in the library define global symbols\)$') + env = os.environ.copy() + # Ref: + # http://www.opensource.apple.com/source/cctools/cctools-809/misc/libtool.c + # The problem with this flag is that it resets the file mtime on the file to + # epoch=0, e.g. 1970-1-1 or 1969-12-31 depending on timezone. + env['ZERO_AR_DATE'] = '1' + libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) + _, err = libtoolout.communicate() + for line in err.splitlines(): + if not libtool_re.match(line) and not libtool_re5.match(line): + print >>sys.stderr, line + # Unconditionally touch the output .a file on the command line if present + # and the command succeeded. A bit hacky. + if not libtoolout.returncode: + for i in range(len(cmd_list) - 1): + if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'): + os.utime(cmd_list[i+1], None) + break + return libtoolout.returncode + + def ExecPackageFramework(self, framework, version): + """Takes a path to Something.framework and the Current version of that and + sets up all the symlinks.""" + # Find the name of the binary based on the part before the ".framework". + binary = os.path.basename(framework).split('.')[0] + + CURRENT = 'Current' + RESOURCES = 'Resources' + VERSIONS = 'Versions' + + if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): + # Binary-less frameworks don't seem to contain symlinks (see e.g. + # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). + return + + # Move into the framework directory to set the symlinks correctly. + pwd = os.getcwd() + os.chdir(framework) + + # Set up the Current version. + self._Relink(version, os.path.join(VERSIONS, CURRENT)) + + # Set up the root symlinks. + self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) + self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) + + # Back to where we were before! + os.chdir(pwd) + + def _Relink(self, dest, link): + """Creates a symlink to |dest| named |link|. If |link| already exists, + it is overwritten.""" + if os.path.lexists(link): + os.remove(link) + os.symlink(dest, link) + + def ExecCompileXcassets(self, keys, *inputs): + """Compiles multiple .xcassets files into a single .car file. + + This invokes 'actool' to compile all the inputs .xcassets files. The + |keys| arguments is a json-encoded dictionary of extra arguments to + pass to 'actool' when the asset catalogs contains an application icon + or a launch image. + + Note that 'actool' does not create the Assets.car file if the asset + catalogs does not contains imageset. + """ + command_line = [ + 'xcrun', 'actool', '--output-format', 'human-readable-text', + '--compress-pngs', '--notices', '--warnings', '--errors', + ] + is_iphone_target = 'IPHONEOS_DEPLOYMENT_TARGET' in os.environ + if is_iphone_target: + platform = os.environ['CONFIGURATION'].split('-')[-1] + if platform not in ('iphoneos', 'iphonesimulator'): + platform = 'iphonesimulator' + command_line.extend([ + '--platform', platform, '--target-device', 'iphone', + '--target-device', 'ipad', '--minimum-deployment-target', + os.environ['IPHONEOS_DEPLOYMENT_TARGET'], '--compile', + os.path.abspath(os.environ['CONTENTS_FOLDER_PATH']), + ]) + else: + command_line.extend([ + '--platform', 'macosx', '--target-device', 'mac', + '--minimum-deployment-target', os.environ['MACOSX_DEPLOYMENT_TARGET'], + '--compile', + os.path.abspath(os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']), + ]) + if keys: + keys = json.loads(keys) + for key, value in keys.iteritems(): + arg_name = '--' + key + if isinstance(value, bool): + if value: + command_line.append(arg_name) + elif isinstance(value, list): + for v in value: + command_line.append(arg_name) + command_line.append(str(v)) + else: + command_line.append(arg_name) + command_line.append(str(value)) + # Note: actool crashes if inputs path are relative, so use os.path.abspath + # to get absolute path name for inputs. + command_line.extend(map(os.path.abspath, inputs)) + subprocess.check_call(command_line) + + def ExecMergeInfoPlist(self, output, *inputs): + """Merge multiple .plist files into a single .plist file.""" + merged_plist = {} + for path in inputs: + plist = self._LoadPlistMaybeBinary(path) + self._MergePlist(merged_plist, plist) + plistlib.writePlist(merged_plist, output) + + def ExecCodeSignBundle(self, key, resource_rules, entitlements, provisioning): + """Code sign a bundle. + + This function tries to code sign an iOS bundle, following the same + algorithm as Xcode: + 1. copy ResourceRules.plist from the user or the SDK into the bundle, + 2. pick the provisioning profile that best match the bundle identifier, + and copy it into the bundle as embedded.mobileprovision, + 3. copy Entitlements.plist from user or SDK next to the bundle, + 4. code sign the bundle. + """ + resource_rules_path = self._InstallResourceRules(resource_rules) + substitutions, overrides = self._InstallProvisioningProfile( + provisioning, self._GetCFBundleIdentifier()) + entitlements_path = self._InstallEntitlements( + entitlements, substitutions, overrides) + subprocess.check_call([ + 'codesign', '--force', '--sign', key, '--resource-rules', + resource_rules_path, '--entitlements', entitlements_path, + os.path.join( + os.environ['TARGET_BUILD_DIR'], + os.environ['FULL_PRODUCT_NAME'])]) + + def _InstallResourceRules(self, resource_rules): + """Installs ResourceRules.plist from user or SDK into the bundle. + + Args: + resource_rules: string, optional, path to the ResourceRules.plist file + to use, default to "${SDKROOT}/ResourceRules.plist" + + Returns: + Path to the copy of ResourceRules.plist into the bundle. + """ + source_path = resource_rules + target_path = os.path.join( + os.environ['BUILT_PRODUCTS_DIR'], + os.environ['CONTENTS_FOLDER_PATH'], + 'ResourceRules.plist') + if not source_path: + source_path = os.path.join( + os.environ['SDKROOT'], 'ResourceRules.plist') + shutil.copy2(source_path, target_path) + return target_path + + def _InstallProvisioningProfile(self, profile, bundle_identifier): + """Installs embedded.mobileprovision into the bundle. + + Args: + profile: string, optional, short name of the .mobileprovision file + to use, if empty or the file is missing, the best file installed + will be used + bundle_identifier: string, value of CFBundleIdentifier from Info.plist + + Returns: + A tuple containing two dictionary: variables substitutions and values + to overrides when generating the entitlements file. + """ + source_path, provisioning_data, team_id = self._FindProvisioningProfile( + profile, bundle_identifier) + target_path = os.path.join( + os.environ['BUILT_PRODUCTS_DIR'], + os.environ['CONTENTS_FOLDER_PATH'], + 'embedded.mobileprovision') + shutil.copy2(source_path, target_path) + substitutions = self._GetSubstitutions(bundle_identifier, team_id + '.') + return substitutions, provisioning_data['Entitlements'] + + def _FindProvisioningProfile(self, profile, bundle_identifier): + """Finds the .mobileprovision file to use for signing the bundle. + + Checks all the installed provisioning profiles (or if the user specified + the PROVISIONING_PROFILE variable, only consult it) and select the most + specific that correspond to the bundle identifier. + + Args: + profile: string, optional, short name of the .mobileprovision file + to use, if empty or the file is missing, the best file installed + will be used + bundle_identifier: string, value of CFBundleIdentifier from Info.plist + + Returns: + A tuple of the path to the selected provisioning profile, the data of + the embedded plist in the provisioning profile and the team identifier + to use for code signing. + + Raises: + SystemExit: if no .mobileprovision can be used to sign the bundle. + """ + profiles_dir = os.path.join( + os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') + if not os.path.isdir(profiles_dir): + print >>sys.stderr, ( + 'cannot find mobile provisioning for %s' % bundle_identifier) + sys.exit(1) + provisioning_profiles = None + if profile: + profile_path = os.path.join(profiles_dir, profile + '.mobileprovision') + if os.path.exists(profile_path): + provisioning_profiles = [profile_path] + if not provisioning_profiles: + provisioning_profiles = glob.glob( + os.path.join(profiles_dir, '*.mobileprovision')) + valid_provisioning_profiles = {} + for profile_path in provisioning_profiles: + profile_data = self._LoadProvisioningProfile(profile_path) + app_id_pattern = profile_data.get( + 'Entitlements', {}).get('application-identifier', '') + for team_identifier in profile_data.get('TeamIdentifier', []): + app_id = '%s.%s' % (team_identifier, bundle_identifier) + if fnmatch.fnmatch(app_id, app_id_pattern): + valid_provisioning_profiles[app_id_pattern] = ( + profile_path, profile_data, team_identifier) + if not valid_provisioning_profiles: + print >>sys.stderr, ( + 'cannot find mobile provisioning for %s' % bundle_identifier) + sys.exit(1) + # If the user has multiple provisioning profiles installed that can be + # used for ${bundle_identifier}, pick the most specific one (ie. the + # provisioning profile whose pattern is the longest). + selected_key = max(valid_provisioning_profiles, key=lambda v: len(v)) + return valid_provisioning_profiles[selected_key] + + def _LoadProvisioningProfile(self, profile_path): + """Extracts the plist embedded in a provisioning profile. + + Args: + profile_path: string, path to the .mobileprovision file + + Returns: + Content of the plist embedded in the provisioning profile as a dictionary. + """ + with tempfile.NamedTemporaryFile() as temp: + subprocess.check_call([ + 'security', 'cms', '-D', '-i', profile_path, '-o', temp.name]) + return self._LoadPlistMaybeBinary(temp.name) + + def _MergePlist(self, merged_plist, plist): + """Merge |plist| into |merged_plist|.""" + for key, value in plist.iteritems(): + if isinstance(value, dict): + merged_value = merged_plist.get(key, {}) + if isinstance(merged_value, dict): + self._MergePlist(merged_value, value) + merged_plist[key] = merged_value + else: + merged_plist[key] = value + else: + merged_plist[key] = value + + def _LoadPlistMaybeBinary(self, plist_path): + """Loads into a memory a plist possibly encoded in binary format. + + This is a wrapper around plistlib.readPlist that tries to convert the + plist to the XML format if it can't be parsed (assuming that it is in + the binary format). + + Args: + plist_path: string, path to a plist file, in XML or binary format + + Returns: + Content of the plist as a dictionary. + """ + try: + # First, try to read the file using plistlib that only supports XML, + # and if an exception is raised, convert a temporary copy to XML and + # load that copy. + return plistlib.readPlist(plist_path) + except: + pass + with tempfile.NamedTemporaryFile() as temp: + shutil.copy2(plist_path, temp.name) + subprocess.check_call(['plutil', '-convert', 'xml1', temp.name]) + return plistlib.readPlist(temp.name) + + def _GetSubstitutions(self, bundle_identifier, app_identifier_prefix): + """Constructs a dictionary of variable substitutions for Entitlements.plist. + + Args: + bundle_identifier: string, value of CFBundleIdentifier from Info.plist + app_identifier_prefix: string, value for AppIdentifierPrefix + + Returns: + Dictionary of substitutions to apply when generating Entitlements.plist. + """ + return { + 'CFBundleIdentifier': bundle_identifier, + 'AppIdentifierPrefix': app_identifier_prefix, + } + + def _GetCFBundleIdentifier(self): + """Extracts CFBundleIdentifier value from Info.plist in the bundle. + + Returns: + Value of CFBundleIdentifier in the Info.plist located in the bundle. + """ + info_plist_path = os.path.join( + os.environ['TARGET_BUILD_DIR'], + os.environ['INFOPLIST_PATH']) + info_plist_data = self._LoadPlistMaybeBinary(info_plist_path) + return info_plist_data['CFBundleIdentifier'] + + def _InstallEntitlements(self, entitlements, substitutions, overrides): + """Generates and install the ${BundleName}.xcent entitlements file. + + Expands variables "$(variable)" pattern in the source entitlements file, + add extra entitlements defined in the .mobileprovision file and the copy + the generated plist to "${BundlePath}.xcent". + + Args: + entitlements: string, optional, path to the Entitlements.plist template + to use, defaults to "${SDKROOT}/Entitlements.plist" + substitutions: dictionary, variable substitutions + overrides: dictionary, values to add to the entitlements + + Returns: + Path to the generated entitlements file. + """ + source_path = entitlements + target_path = os.path.join( + os.environ['BUILT_PRODUCTS_DIR'], + os.environ['PRODUCT_NAME'] + '.xcent') + if not source_path: + source_path = os.path.join( + os.environ['SDKROOT'], + 'Entitlements.plist') + shutil.copy2(source_path, target_path) + data = self._LoadPlistMaybeBinary(target_path) + data = self._ExpandVariables(data, substitutions) + if overrides: + for key in overrides: + if key not in data: + data[key] = overrides[key] + plistlib.writePlist(data, target_path) + return target_path + + def _ExpandVariables(self, data, substitutions): + """Expands variables "$(variable)" in data. + + Args: + data: object, can be either string, list or dictionary + substitutions: dictionary, variable substitutions to perform + + Returns: + Copy of data where each references to "$(variable)" has been replaced + by the corresponding value found in substitutions, or left intact if + the key was not found. + """ + if isinstance(data, str): + for key, value in substitutions.iteritems(): + data = data.replace('$(%s)' % key, value) + return data + if isinstance(data, list): + return [self._ExpandVariables(v, substitutions) for v in data] + if isinstance(data, dict): + return {k: self._ExpandVariables(data[k], substitutions) for k in data} + return data + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/tools/idl_parser/PRESUBMIT.py b/engine/src/flutter/tools/idl_parser/PRESUBMIT.py new file mode 100644 index 0000000000..ff657aa859 --- /dev/null +++ b/engine/src/flutter/tools/idl_parser/PRESUBMIT.py @@ -0,0 +1,15 @@ +# 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. + + +WHITELIST = [ r'^.+_test\.py$' ] + +def CheckChangeOnUpload(input_api, output_api): + return input_api.canned_checks.RunUnitTestsInDirectory( + input_api, output_api, '.', whitelist=WHITELIST) + + +def CheckChangeOnCommit(input_api, output_api): + return input_api.canned_checks.RunUnitTestsInDirectory( + input_api, output_api, '.', whitelist=WHITELIST) diff --git a/engine/src/flutter/tools/idl_parser/__init__.py b/engine/src/flutter/tools/idl_parser/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/engine/src/flutter/tools/idl_parser/idl_lexer.py b/engine/src/flutter/tools/idl_parser/idl_lexer.py new file mode 100755 index 0000000000..abbed37abd --- /dev/null +++ b/engine/src/flutter/tools/idl_parser/idl_lexer.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python +# 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. + +""" Lexer for PPAPI IDL + +The lexer uses the PLY library to build a tokenizer which understands both +WebIDL and Pepper tokens. + +WebIDL, and WebIDL regular expressions can be found at: + http://www.w3.org/TR/2012/CR-WebIDL-20120419/ +PLY can be found at: + http://www.dabeaz.com/ply/ +""" + +import os.path +import sys + +# +# Try to load the ply module, if not, then assume it is in the third_party +# directory. +# +try: + # Disable lint check which fails to find the ply module. + # pylint: disable=F0401 + from ply import lex +except ImportError: + module_path, module_name = os.path.split(__file__) + third_party = os.path.join(module_path, '..', '..', 'third_party') + sys.path.append(third_party) + # pylint: disable=F0401 + from ply import lex + +# +# IDL Lexer +# +class IDLLexer(object): + # 'literals' is a value expected by lex which specifies a list of valid + # literal tokens, meaning the token type and token value are identical. + literals = r'"*.(){}[],;:=+-/~|&^?<>' + + # 't_ignore' contains ignored characters (spaces and tabs) + t_ignore = ' \t' + + # 'tokens' is a value required by lex which specifies the complete list + # of valid token types. + tokens = [ + # Data types + 'float', + 'integer', + 'string', + + # Symbol and keywords types + 'COMMENT', + 'identifier', + + # MultiChar operators + 'ELLIPSIS', + ] + + # 'keywords' is a map of string to token type. All tokens matching + # KEYWORD_OR_SYMBOL are matched against keywords dictionary, to determine + # if the token is actually a keyword. + keywords = { + 'any' : 'ANY', + 'attribute' : 'ATTRIBUTE', + 'boolean' : 'BOOLEAN', + 'byte' : 'BYTE', + 'ByteString' : 'BYTESTRING', + 'callback' : 'CALLBACK', + 'const' : 'CONST', + 'creator' : 'CREATOR', + 'Date' : 'DATE', + 'deleter' : 'DELETER', + 'dictionary' : 'DICTIONARY', + 'DOMString' : 'DOMSTRING', + 'double' : 'DOUBLE', + 'enum' : 'ENUM', + 'exception' : 'EXCEPTION', + 'false' : 'FALSE', + 'float' : 'FLOAT', + 'getter': 'GETTER', + 'implements' : 'IMPLEMENTS', + 'Infinity' : 'INFINITY', + 'inherit' : 'INHERIT', + 'interface' : 'INTERFACE', + 'iterable': 'ITERABLE', + 'legacycaller' : 'LEGACYCALLER', + 'legacyiterable' : 'LEGACYITERABLE', + 'long' : 'LONG', + 'maplike': 'MAPLIKE', + 'Nan' : 'NAN', + 'null' : 'NULL', + 'object' : 'OBJECT', + 'octet' : 'OCTET', + 'optional' : 'OPTIONAL', + 'or' : 'OR', + 'partial' : 'PARTIAL', + 'Promise' : 'PROMISE', + 'readonly' : 'READONLY', + 'RegExp' : 'REGEXP', + 'required' : 'REQUIRED', + 'sequence' : 'SEQUENCE', + 'serializer' : 'SERIALIZER', + 'setlike' : 'SETLIKE', + 'setter': 'SETTER', + 'short' : 'SHORT', + 'static' : 'STATIC', + 'stringifier' : 'STRINGIFIER', + 'typedef' : 'TYPEDEF', + 'true' : 'TRUE', + 'unsigned' : 'UNSIGNED', + 'unrestricted' : 'UNRESTRICTED', + 'void' : 'VOID' + } + + # Token definitions + # + # Lex assumes any value or function in the form of 't_' represents a + # regular expression where a match will emit a token of type . In the + # case of a function, the function is called when a match is made. These + # definitions come from WebIDL. + # + # These need to be methods for lexer construction, despite not using self. + # pylint: disable=R0201 + def t_ELLIPSIS(self, t): + r'\.\.\.' + return t + + # Regex needs to be in the docstring + # pylint: disable=C0301 + def t_float(self, t): + r'-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)' + return t + + def t_integer(self, t): + r'-?([1-9][0-9]*|0[Xx][0-9A-Fa-f]+|0[0-7]*)' + return t + + + # A line ending '\n', we use this to increment the line number + def t_LINE_END(self, t): + r'\n+' + self.AddLines(len(t.value)) + + # We do not process escapes in the IDL strings. Strings are exclusively + # used for attributes and enums, and not used as typical 'C' constants. + def t_string(self, t): + r'"[^"]*"' + t.value = t.value[1:-1] + self.AddLines(t.value.count('\n')) + return t + + # A C or C++ style comment: /* xxx */ or // + def t_COMMENT(self, t): + r'(/\*(.|\n)*?\*/)|(//.*(\n[ \t]*//.*)*)' + self.AddLines(t.value.count('\n')) + return t + + # A symbol or keyword. + def t_KEYWORD_OR_SYMBOL(self, t): + r'_?[A-Za-z][A-Za-z_0-9]*' + + # All non-keywords are assumed to be symbols + t.type = self.keywords.get(t.value, 'identifier') + + # We strip leading underscores so that you can specify symbols with the same + # value as a keywords (E.g. a dictionary named 'interface'). + if t.value[0] == '_': + t.value = t.value[1:] + return t + + def t_ANY_error(self, t): + msg = 'Unrecognized input' + line = self.Lexer().lineno + + # If that line has not been accounted for, then we must have hit + # EoF, so compute the beginning of the line that caused the problem. + if line >= len(self.index): + # Find the offset in the line of the first word causing the issue + word = t.value.split()[0] + offs = self.lines[line - 1].find(word) + # Add the computed line's starting position + self.index.append(self.Lexer().lexpos - offs) + msg = 'Unexpected EoF reached after' + + pos = self.Lexer().lexpos - self.index[line] + out = self.ErrorMessage(line, pos, msg) + sys.stderr.write(out + '\n') + self._lex_errors += 1 + + + def AddLines(self, count): + # Set the lexer position for the beginning of the next line. In the case + # of multiple lines, tokens can not exist on any of the lines except the + # last one, so the recorded value for previous lines are unused. We still + # fill the array however, to make sure the line count is correct. + self.Lexer().lineno += count + for _ in range(count): + self.index.append(self.Lexer().lexpos) + + def FileLineMsg(self, line, msg): + # Generate a message containing the file and line number of a token. + filename = self.Lexer().filename + if filename: + return "%s(%d) : %s" % (filename, line + 1, msg) + return " : %s" % msg + + def SourceLine(self, line, pos): + # Create a source line marker + caret = ' ' * pos + '^' + # We decrement the line number since the array is 0 based while the + # line numbers are 1 based. + return "%s\n%s" % (self.lines[line - 1], caret) + + def ErrorMessage(self, line, pos, msg): + return "\n%s\n%s" % ( + self.FileLineMsg(line, msg), + self.SourceLine(line, pos)) + +# +# Tokenizer +# +# The token function returns the next token provided by IDLLexer for matching +# against the leaf paterns. +# + def token(self): + tok = self.Lexer().token() + if tok: + self.last = tok + return tok + + + def GetTokens(self): + outlist = [] + while True: + t = self.Lexer().token() + if not t: + break + outlist.append(t) + return outlist + + def Tokenize(self, data, filename='__no_file__'): + lexer = self.Lexer() + lexer.lineno = 1 + lexer.filename = filename + lexer.input(data) + self.lines = data.split('\n') + + def KnownTokens(self): + return self.tokens + + def Lexer(self): + if not self._lexobj: + self._lexobj = lex.lex(object=self, lextab=None, optimize=0) + return self._lexobj + + def _AddToken(self, token): + if token in self.tokens: + raise RuntimeError('Same token: ' + token) + self.tokens.append(token) + + def _AddTokens(self, tokens): + for token in tokens: + self._AddToken(token) + + def _AddKeywords(self, keywords): + for key in keywords: + value = key.upper() + self._AddToken(value) + self.keywords[key] = value + + def _DelKeywords(self, keywords): + for key in keywords: + self.tokens.remove(key.upper()) + del self.keywords[key] + + def __init__(self): + self.index = [0] + self._lex_errors = 0 + self.linex = [] + self.filename = None + self.keywords = {} + self.tokens = [] + self._AddTokens(IDLLexer.tokens) + self._AddKeywords(IDLLexer.keywords) + self._lexobj = None + self.last = None + self.lines = None + +# If run by itself, attempt to build the lexer +if __name__ == '__main__': + lexer_object = IDLLexer() diff --git a/engine/src/flutter/tools/idl_parser/idl_lexer_test.py b/engine/src/flutter/tools/idl_parser/idl_lexer_test.py new file mode 100755 index 0000000000..8b20da85fb --- /dev/null +++ b/engine/src/flutter/tools/idl_parser/idl_lexer_test.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# 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. + +import unittest + +from idl_lexer import IDLLexer +from idl_ppapi_lexer import IDLPPAPILexer + +# +# FileToTokens +# +# From a source file generate a list of tokens. +# +def FileToTokens(lexer, filename): + with open(filename, 'rb') as srcfile: + lexer.Tokenize(srcfile.read(), filename) + return lexer.GetTokens() + + +# +# TextToTokens +# +# From a source file generate a list of tokens. +# +def TextToTokens(lexer, text): + lexer.Tokenize(text) + return lexer.GetTokens() + + +class WebIDLLexer(unittest.TestCase): + def setUp(self): + self.lexer = IDLLexer() + self.filenames = [ + 'test_lexer/values.in', + 'test_lexer/keywords.in' + ] + + # + # testRebuildText + # + # From a set of tokens, generate a new source text by joining with a + # single space. The new source is then tokenized and compared against the + # old set. + # + def testRebuildText(self): + for filename in self.filenames: + tokens1 = FileToTokens(self.lexer, filename) + to_text = '\n'.join(['%s' % t.value for t in tokens1]) + tokens2 = TextToTokens(self.lexer, to_text) + + count1 = len(tokens1) + count2 = len(tokens2) + self.assertEqual(count1, count2) + + for i in range(count1): + msg = 'Value %s does not match original %s on line %d of %s.' % ( + tokens2[i].value, tokens1[i].value, tokens1[i].lineno, filename) + self.assertEqual(tokens1[i].value, tokens2[i].value, msg) + + # + # testExpectedType + # + # From a set of tokens pairs, verify the type field of the second matches + # the value of the first, so that: + # integer 123 float 1.1 ... + # will generate a passing test, when the first token has both the type and + # value of the keyword integer and the second has the type of integer and + # value of 123 and so on. + # + def testExpectedType(self): + for filename in self.filenames: + tokens = FileToTokens(self.lexer, filename) + count = len(tokens) + self.assertTrue(count > 0) + self.assertFalse(count & 1) + + index = 0 + while index < count: + expect_type = tokens[index].value + actual_type = tokens[index + 1].type + msg = 'Type %s does not match expected %s on line %d of %s.' % ( + actual_type, expect_type, tokens[index].lineno, filename) + index += 2 + self.assertEqual(expect_type, actual_type, msg) + + +class PepperIDLLexer(WebIDLLexer): + def setUp(self): + self.lexer = IDLPPAPILexer() + self.filenames = [ + 'test_lexer/values_ppapi.in', + 'test_lexer/keywords_ppapi.in' + ] + + +if __name__ == '__main__': + unittest.main() diff --git a/engine/src/flutter/tools/idl_parser/idl_node.py b/engine/src/flutter/tools/idl_parser/idl_node.py new file mode 100755 index 0000000000..e50fc4e1ba --- /dev/null +++ b/engine/src/flutter/tools/idl_parser/idl_node.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +import sys + +# +# IDL Node +# +# IDL Node defines the IDLAttribute and IDLNode objects which are constructed +# by the parser as it processes the various 'productions'. The IDLAttribute +# objects are assigned to the IDLNode's property dictionary instead of being +# applied as children of The IDLNodes, so they do not exist in the final tree. +# The AST of IDLNodes is the output from the parsing state and will be used +# as the source data by the various generators. +# + + +# +# CopyToList +# +# Takes an input item, list, or None, and returns a new list of that set. +def CopyToList(item): + # If the item is 'Empty' make it an empty list + if not item: + item = [] + + # If the item is not a list + if type(item) is not type([]): + item = [item] + + # Make a copy we can modify + return list(item) + + +# IDLSearch +# +# A temporary object used by the parsing process to hold an Extended Attribute +# which will be passed as a child to a standard IDLNode. +# +class IDLSearch(object): + def __init__(self): + self.depth = 0 + + def Enter(self, node): + pass + + def Exit(self, node): + pass + + +# IDLAttribute +# +# A temporary object used by the parsing process to hold an Extended Attribute +# which will be passed as a child to a standard IDLNode. +# +class IDLAttribute(object): + def __init__(self, name, value): + self._cls = 'Property' + self.name = name + self.value = value + + def __str__(self): + return '%s=%s' % (self.name, self.value) + + def GetClass(self): + return self._cls + +# +# IDLNode +# +# This class implements the AST tree, providing the associations between +# parents and children. It also contains a namepsace and propertynode to +# allow for look-ups. IDLNode is derived from IDLRelease, so it is +# version aware. +# +class IDLNode(object): + def __init__(self, cls, filename, lineno, pos, children=None): + self._cls = cls + self._properties = { + 'ERRORS' : [], + 'WARNINGS': [], + 'FILENAME': filename, + 'LINENO' : lineno, + 'POSSITION' : pos, + } + + self._children = [] + self._parent = None + self.AddChildren(children) + +# +# +# + # Return a string representation of this node + def __str__(self): + name = self.GetProperty('NAME','') + return '%s(%s)' % (self._cls, name) + + def GetLogLine(self, msg): + filename, lineno = self.GetFileAndLine() + return '%s(%d) : %s\n' % (filename, lineno, msg) + + # Log an error for this object + def Error(self, msg): + self.GetProperty('ERRORS').append(msg) + sys.stderr.write(self.GetLogLine('error: ' + msg)) + + # Log a warning for this object + def Warning(self, msg): + self.GetProperty('WARNINGS').append(msg) + sys.stdout.write(self.GetLogLine('warning:' + msg)) + + # Return file and line number for where node was defined + def GetFileAndLine(self): + return self.GetProperty('FILENAME'), self.GetProperty('LINENO') + + def GetClass(self): + return self._cls + + def GetName(self): + return self.GetProperty('NAME') + + def GetParent(self): + return self._parent + + def Traverse(self, search, filter_nodes): + if self._cls in filter_nodes: + return '' + + search.Enter(self) + search.depth += 1 + for child in self._children: + child.Traverse(search, filter_nodes) + search.depth -= 1 + search.Exit(self) + + + def Tree(self, filter_nodes=None, accept_props=None): + class DumpTreeSearch(IDLSearch): + def __init__(self, props): + IDLSearch.__init__(self) + self.out = [] + self.props = props + + def Enter(self, node): + tab = ''.rjust(self.depth * 2) + self.out.append(tab + str(node)) + if self.props: + proplist = [] + for key, value in node.GetProperties().iteritems(): + if key in self.props: + proplist.append(tab + ' %s: %s' % (key, str(value))) + if proplist: + self.out.append(tab + ' PROPERTIES') + self.out.extend(proplist) + + if filter_nodes == None: + filter_nodes = ['Comment', 'Copyright'] + + search = DumpTreeSearch(accept_props) + self.Traverse(search, filter_nodes) + return search.out + +# +# Search related functions +# + # Check if node is of a given type + def IsA(self, *typelist): + if self._cls in typelist: + return True + return False + + # Get a list of all children + def GetChildren(self): + return self._children + + def GetListOf(self, *keys): + out = [] + for child in self.GetChildren(): + if child.GetClass() in keys: + out.append(child) + return out + + def GetOneOf(self, *keys): + out = self.GetListOf(*keys) + if out: + return out[0] + return None + + def AddChildren(self, children): + children = CopyToList(children) + for child in children: + if not child: + continue + if type(child) == IDLAttribute: + self.SetProperty(child.name, child.value) + continue + if type(child) == IDLNode: + child._parent = self + self._children.append(child) + continue + raise RuntimeError('Adding child of type %s.\n' % type(child).__name__) + + +# +# Property Functions +# + def SetProperty(self, name, val): + self._properties[name] = val + + def GetProperty(self, name, default=None): + return self._properties.get(name, default) + + def GetProperties(self): + return self._properties diff --git a/engine/src/flutter/tools/idl_parser/idl_parser.py b/engine/src/flutter/tools/idl_parser/idl_parser.py new file mode 100755 index 0000000000..27b1914290 --- /dev/null +++ b/engine/src/flutter/tools/idl_parser/idl_parser.py @@ -0,0 +1,1285 @@ +#!/usr/bin/env python +# 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. + +""" Parser for PPAPI IDL """ + +# +# IDL Parser +# +# The parser is uses the PLY yacc library to build a set of parsing rules based +# on WebIDL. +# +# WebIDL, and WebIDL grammar can be found at: +# http://heycam.github.io/webidl/ +# PLY can be found at: +# http://www.dabeaz.com/ply/ +# +# The parser generates a tree by recursively matching sets of items against +# defined patterns. When a match is made, that set of items is reduced +# to a new item. The new item can provide a match for parent patterns. +# In this way an AST is built (reduced) depth first. +# + +# +# Disable check for line length and Member as Function due to how grammar rules +# are defined with PLY +# +# pylint: disable=R0201 +# pylint: disable=C0301 + +import os.path +import sys +import time + +from idl_lexer import IDLLexer +from idl_node import IDLAttribute, IDLNode + +# +# Try to load the ply module, if not, then assume it is in the third_party +# directory. +# +try: + # Disable lint check which fails to find the ply module. + # pylint: disable=F0401 + from ply import lex + from ply import yacc +except ImportError: + module_path, module_name = os.path.split(__file__) + third_party = os.path.join(module_path, os.par, os.par, 'third_party') + sys.path.append(third_party) + # pylint: disable=F0401 + from ply import lex + from ply import yacc + +# +# ERROR_REMAP +# +# Maps the standard error formula into a more friendly error message. +# +ERROR_REMAP = { + 'Unexpected ")" after "(".' : 'Empty argument list.', + 'Unexpected ")" after ",".' : 'Missing argument.', + 'Unexpected "}" after ",".' : 'Trailing comma in block.', + 'Unexpected "}" after "{".' : 'Unexpected empty block.', + 'Unexpected comment after "}".' : 'Unexpected trailing comment.', + 'Unexpected "{" after keyword "enum".' : 'Enum missing name.', + 'Unexpected "{" after keyword "struct".' : 'Struct missing name.', + 'Unexpected "{" after keyword "interface".' : 'Interface missing name.', +} + + +def Boolean(val): + """Convert to strict boolean type.""" + if val: + return True + return False + + +def ListFromConcat(*items): + """Generate list by concatenating inputs""" + itemsout = [] + for item in items: + if item is None: + continue + if type(item) is not type([]): + itemsout.append(item) + else: + itemsout.extend(item) + + return itemsout + +def ExpandProduction(p): + if type(p) == list: + return '[' + ', '.join([ExpandProduction(x) for x in p]) + ']' + if type(p) == IDLNode: + return 'Node:' + str(p) + if type(p) == IDLAttribute: + return 'Attr:' + str(p) + if type(p) == str: + return 'str:' + p + return '%s:%s' % (p.__class__.__name__, str(p)) + +# TokenTypeName +# +# Generate a string which has the type and value of the token. +# +def TokenTypeName(t): + if t.type == 'SYMBOL': + return 'symbol %s' % t.value + if t.type in ['HEX', 'INT', 'OCT', 'FLOAT']: + return 'value %s' % t.value + if t.type == 'string' : + return 'string "%s"' % t.value + if t.type == 'COMMENT' : + return 'comment' + if t.type == t.value: + return '"%s"' % t.value + if t.type == ',': + return 'Comma' + if t.type == 'identifier': + return 'identifier "%s"' % t.value + return 'keyword "%s"' % t.value + + +# +# IDL Parser +# +# The Parser inherits the from the Lexer to provide PLY with the tokenizing +# definitions. Parsing patterns are encoded as functions where p_ is +# is called any time a patern matching the function documentation is found. +# Paterns are expressed in the form of: +# """ : .... +# | ....""" +# +# Where new item is the result of a match against one or more sets of items +# separated by the "|". +# +# The function is called with an object 'p' where p[0] is the output object +# and p[n] is the set of inputs for positive values of 'n'. Len(p) can be +# used to distinguish between multiple item sets in the pattern. +# +# For more details on parsing refer to the PLY documentation at +# http://www.dabeaz.com/ply/ +# +# The parser is based on the WebIDL standard. See: +# http://heycam.github.io/webidl/#idl-grammar +# +# The various productions are annotated so that the WHOLE number greater than +# zero in the comment denotes the matching WebIDL grammar definition. +# +# Productions with a fractional component in the comment denote additions to +# the WebIDL spec, such as comments. +# + + +class IDLParser(object): +# +# We force all input files to start with two comments. The first comment is a +# Copyright notice followed by a file comment and finally by file level +# productions. +# + # [0] Insert a TOP definition for Copyright and Comments + def p_Top(self, p): + """Top : COMMENT COMMENT Definitions""" + Copyright = self.BuildComment('Copyright', p, 1) + Filedoc = self.BuildComment('Comment', p, 2) + p[0] = ListFromConcat(Copyright, Filedoc, p[3]) + + # [0.1] Add support for Multiple COMMENTS + def p_Comments(self, p): + """Comments : CommentsRest""" + if len(p) > 1: + p[0] = p[1] + + # [0.2] Produce a COMMENT and aggregate sibling comments + def p_CommentsRest(self, p): + """CommentsRest : COMMENT CommentsRest + | """ + if len(p) > 1: + p[0] = ListFromConcat(self.BuildComment('Comment', p, 1), p[2]) + + +# +#The parser is based on the WebIDL standard. See: +# http://heycam.github.io/webidl/#idl-grammar +# + # [1] + def p_Definitions(self, p): + """Definitions : ExtendedAttributeList Definition Definitions + | """ + if len(p) > 1: + p[2].AddChildren(p[1]) + p[0] = ListFromConcat(p[2], p[3]) + + # [2] + def p_Definition(self, p): + """Definition : CallbackOrInterface + | Partial + | Dictionary + | Exception + | Enum + | Typedef + | ImplementsStatement""" + p[0] = p[1] + + # [2.1] Error recovery for definition + def p_DefinitionError(self, p): + """Definition : error ';'""" + p[0] = self.BuildError(p, 'Definition') + + # [3] + def p_CallbackOrInterface(self, p): + """CallbackOrInterface : CALLBACK CallbackRestOrInterface + | Interface""" + if len(p) > 2: + p[0] = p[2] + else: + p[0] = p[1] + + # [4] + def p_CallbackRestOrInterface(self, p): + """CallbackRestOrInterface : CallbackRest + | Interface""" + p[0] = p[1] + + # [5] + def p_Interface(self, p): + """Interface : INTERFACE identifier Inheritance '{' InterfaceMembers '}' ';'""" + p[0] = self.BuildNamed('Interface', p, 2, ListFromConcat(p[3], p[5])) + + # [5.1] Error recovery for interface. + def p_InterfaceError(self, p): + """Interface : INTERFACE identifier Inheritance '{' error""" + p[0] = self.BuildError(p, 'Interface') + + # [6] + def p_Partial(self, p): + """Partial : PARTIAL PartialDefinition""" + p[2].AddChildren(self.BuildTrue('Partial')) + p[0] = p[2] + + # [6.1] Error recovery for Partial + def p_PartialError(self, p): + """Partial : PARTIAL error""" + p[0] = self.BuildError(p, 'Partial') + + # [7] + def p_PartialDefinition(self, p): + """PartialDefinition : PartialDictionary + | PartialInterface""" + p[0] = p[1] + + # [8] + def p_PartialInterface(self, p): + """PartialInterface : INTERFACE identifier '{' InterfaceMembers '}' ';'""" + p[0] = self.BuildNamed('Interface', p, 2, p[4]) + + # [9] + def p_InterfaceMembers(self, p): + """InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers + |""" + if len(p) > 1: + p[2].AddChildren(p[1]) + p[0] = ListFromConcat(p[2], p[3]) + + # [9.1] Error recovery for InterfaceMembers + def p_InterfaceMembersError(self, p): + """InterfaceMembers : error""" + p[0] = self.BuildError(p, 'InterfaceMembers') + + # [10] Removed unsupported: Serializer + def p_InterfaceMember(self, p): + """InterfaceMember : Const + | Operation + | Serializer + | Stringifier + | StaticMember + | Iterable + | ReadonlyMember + | ReadWriteAttribute + | ReadWriteMaplike + | ReadWriteSetlike""" + p[0] = p[1] + + # [11] + def p_Dictionary(self, p): + """Dictionary : DICTIONARY identifier Inheritance '{' DictionaryMembers '}' ';'""" + p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[3], p[5])) + + # [11.1] Error recovery for regular Dictionary + def p_DictionaryError(self, p): + """Dictionary : DICTIONARY error ';'""" + p[0] = self.BuildError(p, 'Dictionary') + + # [11.2] Error recovery for regular Dictionary + # (for errors inside dictionary definition) + def p_DictionaryError2(self, p): + """Dictionary : DICTIONARY identifier Inheritance '{' error""" + p[0] = self.BuildError(p, 'Dictionary') + + # [12] + def p_DictionaryMembers(self, p): + """DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers + |""" + if len(p) > 1: + p[2].AddChildren(p[1]) + p[0] = ListFromConcat(p[2], p[3]) + + # [13] + def p_DictionaryMember(self, p): + """DictionaryMember : Type identifier Default ';'""" + p[0] = self.BuildNamed('Key', p, 2, ListFromConcat(p[1], p[3])) + + # [14] NOT IMPLEMENTED (Required) + + # [15] + def p_PartialDictionary(self, p): + """PartialDictionary : DICTIONARY identifier '{' DictionaryMembers '}' ';'""" + partial = self.BuildTrue('Partial') + p[0] = self.BuildNamed('Dictionary', p, 2, ListFromConcat(p[4], partial)) + + # [15.1] Error recovery for Partial Dictionary + def p_PartialDictionaryError(self, p): + """PartialDictionary : DICTIONARY error ';'""" + p[0] = self.BuildError(p, 'PartialDictionary') + + # [16] + def p_Default(self, p): + """Default : '=' DefaultValue + |""" + if len(p) > 1: + p[0] = self.BuildProduction('Default', p, 2, p[2]) + + # [17] + def p_DefaultValue(self, p): + """DefaultValue : ConstValue + | string + | '[' ']'""" + if len(p) == 3: + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'sequence'), + self.BuildAttribute('VALUE', '[]')) + elif type(p[1]) == str: + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'DOMString'), + self.BuildAttribute('NAME', p[1])) + else: + p[0] = p[1] + + # [] - Not specified + def p_Exception(self, p): + """Exception : EXCEPTION identifier Inheritance '{' ExceptionMembers '}' ';'""" + p[0] = self.BuildNamed('Exception', p, 2, ListFromConcat(p[3], p[5])) + + # [] - Not specified + def p_ExceptionMembers(self, p): + """ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers + |""" + if len(p) > 1: + p[2].AddChildren(p[1]) + p[0] = ListFromConcat(p[2], p[3]) + + # [.1] Error recovery for ExceptionMembers - Not specified + def p_ExceptionMembersError(self, p): + """ExceptionMembers : error""" + p[0] = self.BuildError(p, 'ExceptionMembers') + + # [18] + def p_Inheritance(self, p): + """Inheritance : ':' identifier + |""" + if len(p) > 1: + p[0] = self.BuildNamed('Inherit', p, 2) + + # [19] + def p_Enum(self, p): + """Enum : ENUM identifier '{' EnumValueList '}' ';'""" + p[0] = self.BuildNamed('Enum', p, 2, p[4]) + + # [19.1] Error recovery for Enums + def p_EnumError(self, p): + """Enum : ENUM error ';'""" + p[0] = self.BuildError(p, 'Enum') + + # [20] + def p_EnumValueList(self, p): + """EnumValueList : ExtendedAttributeList string EnumValueListComma""" + enum = self.BuildNamed('EnumItem', p, 2, p[1]) + p[0] = ListFromConcat(enum, p[3]) + + # [21] + def p_EnumValueListComma(self, p): + """EnumValueListComma : ',' EnumValueListString + |""" + if len(p) > 1: + p[0] = p[2] + + # [22] + def p_EnumValueListString(self, p): + """EnumValueListString : ExtendedAttributeList string EnumValueListComma + |""" + if len(p) > 1: + enum = self.BuildNamed('EnumItem', p, 2, p[1]) + p[0] = ListFromConcat(enum, p[3]) + + # [23] + def p_CallbackRest(self, p): + """CallbackRest : identifier '=' ReturnType '(' ArgumentList ')' ';'""" + arguments = self.BuildProduction('Arguments', p, 4, p[5]) + p[0] = self.BuildNamed('Callback', p, 1, ListFromConcat(p[3], arguments)) + + # [24] + def p_Typedef(self, p): + """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'""" + p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3])) + + # [24.1] Error recovery for Typedefs + def p_TypedefError(self, p): + """Typedef : TYPEDEF error ';'""" + p[0] = self.BuildError(p, 'Typedef') + + # [25] + def p_ImplementsStatement(self, p): + """ImplementsStatement : identifier IMPLEMENTS identifier ';'""" + name = self.BuildAttribute('REFERENCE', p[3]) + p[0] = self.BuildNamed('Implements', p, 1, name) + + # [26] + def p_Const(self, p): + """Const : CONST ConstType identifier '=' ConstValue ';'""" + value = self.BuildProduction('Value', p, 5, p[5]) + p[0] = self.BuildNamed('Const', p, 3, ListFromConcat(p[2], value)) + + # [27] + def p_ConstValue(self, p): + """ConstValue : BooleanLiteral + | FloatLiteral + | integer + | null""" + if type(p[1]) == str: + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), + self.BuildAttribute('NAME', p[1])) + else: + p[0] = p[1] + + # [27.1] Add definition for NULL + def p_null(self, p): + """null : NULL""" + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'NULL'), + self.BuildAttribute('NAME', 'NULL')) + + # [28] + def p_BooleanLiteral(self, p): + """BooleanLiteral : TRUE + | FALSE""" + value = self.BuildAttribute('VALUE', Boolean(p[1] == 'true')) + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'boolean'), value) + + # [29] + def p_FloatLiteral(self, p): + """FloatLiteral : float + | '-' INFINITY + | INFINITY + | NAN """ + if len(p) > 2: + val = '-Infinity' + else: + val = p[1] + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'float'), + self.BuildAttribute('VALUE', val)) + + # [30] + def p_Serializer(self, p): + """Serializer : SERIALIZER SerializerRest""" + p[0] = self.BuildProduction('Serializer', p, 1, p[2]) + + # [31] + # TODO(jl): This adds ReturnType and ';', missing from the spec's grammar. + # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361 + def p_SerializerRest(self, p): + """SerializerRest : ReturnType OperationRest + | '=' SerializationPattern ';' + | ';'""" + if len(p) == 3: + p[2].AddChildren(p[1]) + p[0] = p[2] + elif len(p) == 4: + p[0] = p[2] + + # [32] + def p_SerializationPattern(self, p): + """SerializationPattern : '{' SerializationPatternMap '}' + | '[' SerializationPatternList ']' + | identifier""" + if len(p) > 2: + p[0] = p[2] + else: + p[0] = self.BuildAttribute('ATTRIBUTE', p[1]) + + # [33] + # TODO(jl): This adds the "ATTRIBUTE" and "INHERIT ',' ATTRIBUTE" variants, + # missing from the spec's grammar. + # https://www.w3.org/Bugs/Public/show_bug.cgi?id=20361 + def p_SerializationPatternMap(self, p): + """SerializationPatternMap : GETTER + | ATTRIBUTE + | INHERIT ',' ATTRIBUTE + | INHERIT Identifiers + | identifier Identifiers + |""" + p[0] = self.BuildProduction('Map', p, 0) + if len(p) == 4: + p[0].AddChildren(self.BuildTrue('INHERIT')) + p[0].AddChildren(self.BuildTrue('ATTRIBUTE')) + elif len(p) > 1: + if p[1] == 'getter': + p[0].AddChildren(self.BuildTrue('GETTER')) + elif p[1] == 'attribute': + p[0].AddChildren(self.BuildTrue('ATTRIBUTE')) + else: + if p[1] == 'inherit': + p[0].AddChildren(self.BuildTrue('INHERIT')) + attributes = p[2] + else: + attributes = ListFromConcat(p[1], p[2]) + p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes)) + + # [34] + def p_SerializationPatternList(self, p): + """SerializationPatternList : GETTER + | identifier Identifiers + |""" + p[0] = self.BuildProduction('List', p, 0) + if len(p) > 1: + if p[1] == 'getter': + p[0].AddChildren(self.BuildTrue('GETTER')) + else: + attributes = ListFromConcat(p[1], p[2]) + p[0].AddChildren(self.BuildAttribute('ATTRIBUTES', attributes)) + + # [35] + def p_Stringifier(self, p): + """Stringifier : STRINGIFIER StringifierRest""" + p[0] = self.BuildProduction('Stringifier', p, 1, p[2]) + + # [36] + def p_StringifierRest(self, p): + """StringifierRest : AttributeRest + | ReturnType OperationRest + | ';'""" + if len(p) == 3: + p[2].AddChildren(p[1]) + p[0] = p[2] + elif p[1] != ';': + p[0] = p[1] + + # [37] + def p_StaticMember(self, p): + """StaticMember : STATIC StaticMemberRest""" + p[2].AddChildren(self.BuildTrue('STATIC')) + p[0] = p[2] + + # [38] + def p_StaticMemberRest(self, p): + """StaticMemberRest : ReadOnly AttributeRest + | ReturnType OperationRest""" + if len(p) == 2: + p[0] = p[1] + else: + p[2].AddChildren(p[1]) + p[0] = p[2] + + # [39] + def p_ReadonlyMember(self, p): + """ReadonlyMember : READONLY ReadonlyMemberRest""" + p[2].AddChildren(self.BuildTrue('READONLY')) + p[0] = p[2] + + # [40] + def p_ReadonlyMemberRest(self, p): + """ReadonlyMemberRest : AttributeRest + | MaplikeRest + | SetlikeRest""" + p[0] = p[1] + + # [41] + def p_ReadWriteAttribute(self, p): + """ReadWriteAttribute : INHERIT ReadOnly AttributeRest + | AttributeRest""" + if len(p) > 2: + inherit = self.BuildTrue('INHERIT') + p[3].AddChildren(ListFromConcat(inherit, p[2])) + p[0] = p[3] + else: + p[0] = p[1] + + # [42] + def p_AttributeRest(self, p): + """AttributeRest : ATTRIBUTE Type AttributeName ';'""" + p[0] = self.BuildNamed('Attribute', p, 3, p[2]) + + # [43] + def p_AttributeName(self, p): + """AttributeName : AttributeNameKeyword + | identifier""" + p[0] = p[1] + + # [44] + def p_AttributeNameKeyword(self, p): + """AttributeNameKeyword : REQUIRED""" + p[0] = p[1] + + # [45] Unreferenced in the specification + + # [46] + def p_ReadOnly(self, p): + """ReadOnly : READONLY + |""" + if len(p) > 1: + p[0] = self.BuildTrue('READONLY') + + # [47] + def p_Operation(self, p): + """Operation : ReturnType OperationRest + | SpecialOperation""" + if len(p) == 3: + p[2].AddChildren(p[1]) + p[0] = p[2] + else: + p[0] = p[1] + + # [48] + def p_SpecialOperation(self, p): + """SpecialOperation : Special Specials ReturnType OperationRest""" + p[4].AddChildren(ListFromConcat(p[1], p[2], p[3])) + p[0] = p[4] + + # [49] + def p_Specials(self, p): + """Specials : Special Specials + | """ + if len(p) > 1: + p[0] = ListFromConcat(p[1], p[2]) + + # [50] + def p_Special(self, p): + """Special : GETTER + | SETTER + | CREATOR + | DELETER + | LEGACYCALLER""" + p[0] = self.BuildTrue(p[1].upper()) + + # [51] + def p_OperationRest(self, p): + """OperationRest : OptionalIdentifier '(' ArgumentList ')' ';'""" + arguments = self.BuildProduction('Arguments', p, 2, p[3]) + p[0] = self.BuildNamed('Operation', p, 1, arguments) + + # [52] + def p_OptionalIdentifier(self, p): + """OptionalIdentifier : identifier + |""" + if len(p) > 1: + p[0] = p[1] + else: + p[0] = '_unnamed_' + + # [53] + def p_ArgumentList(self, p): + """ArgumentList : Argument Arguments + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[1], p[2]) + + # [53.1] ArgumentList error recovery + def p_ArgumentListError(self, p): + """ArgumentList : error """ + p[0] = self.BuildError(p, 'ArgumentList') + + # [54] + def p_Arguments(self, p): + """Arguments : ',' Argument Arguments + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[2], p[3]) + + # [54.1] Arguments error recovery + def p_ArgumentsError(self, p): + """Arguments : ',' error""" + p[0] = self.BuildError(p, 'Arguments') + + # [55] + def p_Argument(self, p): + """Argument : ExtendedAttributeList OptionalOrRequiredArgument""" + p[2].AddChildren(p[1]) + p[0] = p[2] + + # [56] + def p_OptionalOrRequiredArgument(self, p): + """OptionalOrRequiredArgument : OPTIONAL Type ArgumentName Default + | Type Ellipsis ArgumentName""" + if len(p) > 4: + arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[2], p[4])) + arg.AddChildren(self.BuildTrue('OPTIONAL')) + else: + arg = self.BuildNamed('Argument', p, 3, ListFromConcat(p[1], p[2])) + p[0] = arg + + # [57] + def p_ArgumentName(self, p): + """ArgumentName : ArgumentNameKeyword + | identifier""" + p[0] = p[1] + + # [58] + def p_Ellipsis(self, p): + """Ellipsis : ELLIPSIS + |""" + if len(p) > 1: + p[0] = self.BuildNamed('Argument', p, 1) + p[0].AddChildren(self.BuildTrue('ELLIPSIS')) + + # [] Unspecified + def p_ExceptionMember(self, p): + """ExceptionMember : Const + | ExceptionField""" + p[0] = p[1] + + # [] Unspecified + def p_ExceptionField(self, p): + """ExceptionField : Type identifier ';'""" + p[0] = self.BuildNamed('ExceptionField', p, 2, p[1]) + + # [] Error recovery for ExceptionMembers - Unspecified + def p_ExceptionFieldError(self, p): + """ExceptionField : error""" + p[0] = self.BuildError(p, 'ExceptionField') + + # [59] + def p_Iterable(self, p): + """Iterable : ITERABLE '<' Type OptionalType '>' ';' + | LEGACYITERABLE '<' Type '>' ';'""" + if len(p) > 6: + childlist = ListFromConcat(p[3], p[4]) + p[0] = self.BuildProduction('Iterable', p, 2, childlist) + else: + p[0] = self.BuildProduction('LegacyIterable', p, 2, p[3]) + + # [60] + def p_OptionalType(self, p): + """OptionalType : ',' Type + |""" + if len(p) > 1: + p[0] = p[2] + + # [61] + def p_ReadWriteMaplike(self, p): + """ReadWriteMaplike : MaplikeRest""" + p[0] = p[1] + + # [62] + def p_ReadWriteSetlike(self, p): + """ReadWriteSetlike : SetlikeRest""" + p[0] = p[1] + + # [63] + def p_MaplikeRest(self, p): + """MaplikeRest : MAPLIKE '<' Type ',' Type '>' ';'""" + childlist = ListFromConcat(p[3], p[5]) + p[0] = self.BuildProduction('Maplike', p, 2, childlist) + + # [64] + def p_SetlikeRest(self, p): + """SetlikeRest : SETLIKE '<' Type '>' ';'""" + p[0] = self.BuildProduction('Setlike', p, 2, p[3]) + + # [65] No comment version for mid statement attributes. + def p_ExtendedAttributeListNoComments(self, p): + """ExtendedAttributeListNoComments : '[' ExtendedAttribute ExtendedAttributes ']' + | """ + if len(p) > 2: + items = ListFromConcat(p[2], p[3]) + p[0] = self.BuildProduction('ExtAttributes', p, 1, items) + + # [65.1] Add optional comment field for start of statements. + def p_ExtendedAttributeList(self, p): + """ExtendedAttributeList : Comments '[' ExtendedAttribute ExtendedAttributes ']' + | Comments """ + if len(p) > 2: + items = ListFromConcat(p[3], p[4]) + attribs = self.BuildProduction('ExtAttributes', p, 2, items) + p[0] = ListFromConcat(p[1], attribs) + else: + p[0] = p[1] + + # [66] + def p_ExtendedAttributes(self, p): + """ExtendedAttributes : ',' ExtendedAttribute ExtendedAttributes + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[2], p[3]) + + # We only support: + # [ identifier ] + # [ identifier ( ArgumentList ) ] + # [ identifier = identifier ] + # [ identifier = ( IdentifierList ) ] + # [ identifier = identifier ( ArgumentList ) ] + # [66] map directly to [91-93, 95] + # [67-69, 71] are unsupported + def p_ExtendedAttribute(self, p): + """ExtendedAttribute : ExtendedAttributeNoArgs + | ExtendedAttributeArgList + | ExtendedAttributeIdent + | ExtendedAttributeIdentList + | ExtendedAttributeNamedArgList""" + p[0] = p[1] + + # [71] + def p_ArgumentNameKeyword(self, p): + """ArgumentNameKeyword : ATTRIBUTE + | CALLBACK + | CONST + | CREATOR + | DELETER + | DICTIONARY + | ENUM + | EXCEPTION + | GETTER + | IMPLEMENTS + | INHERIT + | LEGACYCALLER + | PARTIAL + | SERIALIZER + | SETTER + | STATIC + | STRINGIFIER + | TYPEDEF + | UNRESTRICTED""" + p[0] = p[1] + + # [72] NOT IMPLEMENTED (OtherOrComma) + + # [73] + def p_Type(self, p): + """Type : SingleType + | UnionType TypeSuffix""" + if len(p) == 2: + p[0] = self.BuildProduction('Type', p, 1, p[1]) + else: + p[0] = self.BuildProduction('Type', p, 1, ListFromConcat(p[1], p[2])) + + # [74] + def p_SingleType(self, p): + """SingleType : NonAnyType + | ANY TypeSuffixStartingWithArray""" + if len(p) == 2: + p[0] = p[1] + else: + p[0] = ListFromConcat(self.BuildProduction('Any', p, 1), p[2]) + + # [75] + def p_UnionType(self, p): + """UnionType : '(' UnionMemberType OR UnionMemberType UnionMemberTypes ')'""" + + # [76] + def p_UnionMemberType(self, p): + """UnionMemberType : NonAnyType + | UnionType TypeSuffix + | ANY '[' ']' TypeSuffix""" + # [77] + def p_UnionMemberTypes(self, p): + """UnionMemberTypes : OR UnionMemberType UnionMemberTypes + |""" + + # [78] Moved BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP to PrimitiveType + # Moving all built-in types into PrimitiveType makes it easier to + # differentiate between them and 'identifier', since p[1] would be a string in + # both cases. + def p_NonAnyType(self, p): + """NonAnyType : PrimitiveType TypeSuffix + | PromiseType Null + | identifier TypeSuffix + | SEQUENCE '<' Type '>' Null""" + if len(p) == 3: + if type(p[1]) == str: + typeref = self.BuildNamed('Typeref', p, 1) + else: + typeref = p[1] + p[0] = ListFromConcat(typeref, p[2]) + + if len(p) == 6: + p[0] = self.BuildProduction('Sequence', p, 1, ListFromConcat(p[3], p[5])) + + # [79] NOT IMPLEMENTED (BufferRelatedType) + + # [80] + def p_ConstType(self, p): + """ConstType : PrimitiveType Null + | identifier Null""" + if type(p[1]) == str: + p[0] = self.BuildNamed('Typeref', p, 1, p[2]) + else: + p[1].AddChildren(p[2]) + p[0] = p[1] + + + # [81] Added BYTESTRING, DOMSTRING, OBJECT, DATE, REGEXP + def p_PrimitiveType(self, p): + """PrimitiveType : UnsignedIntegerType + | UnrestrictedFloatType + | BOOLEAN + | BYTE + | OCTET + | BYTESTRING + | DOMSTRING + | OBJECT + | DATE + | REGEXP""" + if type(p[1]) == str: + p[0] = self.BuildNamed('PrimitiveType', p, 1) + else: + p[0] = p[1] + + + # [82] + def p_UnrestrictedFloatType(self, p): + """UnrestrictedFloatType : UNRESTRICTED FloatType + | FloatType""" + if len(p) == 2: + typeref = self.BuildNamed('PrimitiveType', p, 1) + else: + typeref = self.BuildNamed('PrimitiveType', p, 2) + typeref.AddChildren(self.BuildTrue('UNRESTRICTED')) + p[0] = typeref + + + # [83] + def p_FloatType(self, p): + """FloatType : FLOAT + | DOUBLE""" + p[0] = p[1] + + # [84] + def p_UnsignedIntegerType(self, p): + """UnsignedIntegerType : UNSIGNED IntegerType + | IntegerType""" + if len(p) == 2: + p[0] = p[1] + else: + p[0] = 'unsigned ' + p[2] + + # [85] + def p_IntegerType(self, p): + """IntegerType : SHORT + | LONG OptionalLong""" + if len(p) == 2: + p[0] = p[1] + else: + p[0] = p[1] + p[2] + + # [86] + def p_OptionalLong(self, p): + """OptionalLong : LONG + | """ + if len(p) > 1: + p[0] = ' ' + p[1] + else: + p[0] = '' + + # [87] Add unqualified Promise + def p_PromiseType(self, p): + """PromiseType : PROMISE '<' ReturnType '>' + | PROMISE""" + if len(p) == 2: + # Promise without resolution type is not specified in the Web IDL spec. + # As it is used in some specs and in the blink implementation, + # we allow that here. + resolution_type = self.BuildProduction('Type', p, 1, + self.BuildProduction('Any', p, 1)) + p[0] = self.BuildNamed('Promise', p, 1, resolution_type) + else: + p[0] = self.BuildNamed('Promise', p, 1, p[3]) + + # [88] Add support for sized array + def p_TypeSuffix(self, p): + """TypeSuffix : '[' integer ']' TypeSuffix + | '[' ']' TypeSuffix + | '?' TypeSuffixStartingWithArray + | """ + if len(p) == 5: + p[0] = self.BuildNamed('Array', p, 2, p[4]) + + if len(p) == 4: + p[0] = self.BuildProduction('Array', p, 1, p[3]) + + if len(p) == 3: + p[0] = ListFromConcat(self.BuildTrue('NULLABLE'), p[2]) + + + # [89] + def p_TypeSuffixStartingWithArray(self, p): + """TypeSuffixStartingWithArray : '[' ']' TypeSuffix + | """ + if len(p) > 1: + p[0] = self.BuildProduction('Array', p, 0, p[3]) + + # [90] + def p_Null(self, p): + """Null : '?' + |""" + if len(p) > 1: + p[0] = self.BuildTrue('NULLABLE') + + # [91] + def p_ReturnType(self, p): + """ReturnType : Type + | VOID""" + if p[1] == 'void': + p[0] = self.BuildProduction('Type', p, 1) + p[0].AddChildren(self.BuildNamed('PrimitiveType', p, 1)) + else: + p[0] = p[1] + + # [92] + def p_IdentifierList(self, p): + """IdentifierList : identifier Identifiers""" + p[0] = ListFromConcat(p[1], p[2]) + + # [93] + def p_Identifiers(self, p): + """Identifiers : ',' identifier Identifiers + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[2], p[3]) + + # [94] + def p_ExtendedAttributeNoArgs(self, p): + """ExtendedAttributeNoArgs : identifier""" + p[0] = self.BuildNamed('ExtAttribute', p, 1) + + # [95] + def p_ExtendedAttributeArgList(self, p): + """ExtendedAttributeArgList : identifier '(' ArgumentList ')'""" + arguments = self.BuildProduction('Arguments', p, 2, p[3]) + p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments) + + # [96] + def p_ExtendedAttributeIdent(self, p): + """ExtendedAttributeIdent : identifier '=' identifier""" + value = self.BuildAttribute('VALUE', p[3]) + p[0] = self.BuildNamed('ExtAttribute', p, 1, value) + + # [97] + def p_ExtendedAttributeIdentList(self, p): + """ExtendedAttributeIdentList : identifier '=' '(' IdentifierList ')'""" + value = self.BuildAttribute('VALUE', p[4]) + p[0] = self.BuildNamed('ExtAttribute', p, 1, value) + + # [98] + def p_ExtendedAttributeNamedArgList(self, p): + """ExtendedAttributeNamedArgList : identifier '=' identifier '(' ArgumentList ')'""" + args = self.BuildProduction('Arguments', p, 4, p[5]) + value = self.BuildNamed('Call', p, 3, args) + p[0] = self.BuildNamed('ExtAttribute', p, 1, value) + +# +# Parser Errors +# +# p_error is called whenever the parser can not find a pattern match for +# a set of items from the current state. The p_error function defined here +# is triggered logging an error, and parsing recovery happens as the +# p__error functions defined above are called. This allows the parser +# to continue so as to capture more than one error per file. +# + def p_error(self, t): + if t: + lineno = t.lineno + pos = t.lexpos + prev = self.yaccobj.symstack[-1] + if type(prev) == lex.LexToken: + msg = "Unexpected %s after %s." % ( + TokenTypeName(t), TokenTypeName(prev)) + else: + msg = "Unexpected %s." % (t.value) + else: + last = self.LastToken() + lineno = last.lineno + pos = last.lexpos + msg = "Unexpected end of file after %s." % TokenTypeName(last) + self.yaccobj.restart() + + # Attempt to remap the error to a friendlier form + if msg in ERROR_REMAP: + msg = ERROR_REMAP[msg] + + self._last_error_msg = msg + self._last_error_lineno = lineno + self._last_error_pos = pos + + def Warn(self, node, msg): + sys.stdout.write(node.GetLogLine(msg)) + self.parse_warnings += 1 + + def LastToken(self): + return self.lexer.last + + def __init__(self, lexer, verbose=False, debug=False, mute_error=False): + self.lexer = lexer + self.tokens = lexer.KnownTokens() + self.yaccobj = yacc.yacc(module=self, tabmodule=None, debug=debug, + optimize=0, write_tables=0) + self.parse_debug = debug + self.verbose = verbose + self.mute_error = mute_error + self._parse_errors = 0 + self._parse_warnings = 0 + self._last_error_msg = None + self._last_error_lineno = 0 + self._last_error_pos = 0 + + +# +# BuildProduction +# +# Production is the set of items sent to a grammar rule resulting in a new +# item being returned. +# +# p - Is the Yacc production object containing the stack of items +# index - Index into the production of the name for the item being produced. +# cls - The type of item being producted +# childlist - The children of the new item + def BuildProduction(self, cls, p, index, childlist=None): + try: + if not childlist: + childlist = [] + + filename = self.lexer.Lexer().filename + lineno = p.lineno(index) + pos = p.lexpos(index) + out = IDLNode(cls, filename, lineno, pos, childlist) + return out + except: + print 'Exception while parsing:' + for num, item in enumerate(p): + print ' [%d] %s' % (num, ExpandProduction(item)) + if self.LastToken(): + print 'Last token: %s' % str(self.LastToken()) + raise + + def BuildNamed(self, cls, p, index, childlist=None): + childlist = ListFromConcat(childlist) + childlist.append(self.BuildAttribute('NAME', p[index])) + return self.BuildProduction(cls, p, index, childlist) + + def BuildComment(self, cls, p, index): + name = p[index] + + # Remove comment markers + lines = [] + if name[:2] == '//': + # For C++ style, remove any leading whitespace and the '//' marker from + # each line. + form = 'cc' + for line in name.split('\n'): + start = line.find('//') + lines.append(line[start+2:]) + else: + # For C style, remove ending '*/'' + form = 'c' + for line in name[:-2].split('\n'): + # Remove characters until start marker for this line '*' if found + # otherwise it should be blank. + offs = line.find('*') + if offs >= 0: + line = line[offs + 1:].rstrip() + else: + line = '' + lines.append(line) + name = '\n'.join(lines) + childlist = [self.BuildAttribute('NAME', name), + self.BuildAttribute('FORM', form)] + return self.BuildProduction(cls, p, index, childlist) + +# +# BuildError +# +# Build and Errror node as part of the recovery process. +# +# + def BuildError(self, p, prod): + self._parse_errors += 1 + name = self.BuildAttribute('NAME', self._last_error_msg) + line = self.BuildAttribute('LINE', self._last_error_lineno) + pos = self.BuildAttribute('POS', self._last_error_pos) + prod = self.BuildAttribute('PROD', prod) + + node = self.BuildProduction('Error', p, 1, + ListFromConcat(name, line, pos, prod)) + if not self.mute_error: + node.Error(self._last_error_msg) + + return node + +# +# BuildAttribute +# +# An ExtendedAttribute is a special production that results in a property +# which is applied to the adjacent item. Attributes have no children and +# instead represent key/value pairs. +# + def BuildAttribute(self, key, val): + return IDLAttribute(key, val) + + def BuildFalse(self, key): + return IDLAttribute(key, Boolean(False)) + + def BuildTrue(self, key): + return IDLAttribute(key, Boolean(True)) + + def GetErrors(self): + # Access lexer errors, despite being private + # pylint: disable=W0212 + return self._parse_errors + self.lexer._lex_errors + +# +# ParseData +# +# Attempts to parse the current data loaded in the lexer. +# + def ParseText(self, filename, data): + self._parse_errors = 0 + self._parse_warnings = 0 + self._last_error_msg = None + self._last_error_lineno = 0 + self._last_error_pos = 0 + + try: + self.lexer.Tokenize(data, filename) + nodes = self.yaccobj.parse(lexer=self.lexer) or [] + name = self.BuildAttribute('NAME', filename) + return IDLNode('File', filename, 0, 0, nodes + [name]) + + except lex.LexError as lexError: + sys.stderr.write('Error in token: %s\n' % str(lexError)) + return None + + + +def ParseFile(parser, filename): + """Parse a file and return a File type of node.""" + with open(filename) as fileobject: + try: + out = parser.ParseText(filename, fileobject.read()) + out.SetProperty('DATETIME', time.ctime(os.path.getmtime(filename))) + out.SetProperty('ERRORS', parser.GetErrors()) + return out + + except Exception as e: + last = parser.LastToken() + sys.stderr.write('%s(%d) : Internal parsing error\n\t%s.\n' % ( + filename, last.lineno, str(e))) + + +def main(argv): + nodes = [] + parser = IDLParser(IDLLexer()) + errors = 0 + for filename in argv: + filenode = ParseFile(parser, filename) + if (filenode): + errors += filenode.GetProperty('ERRORS') + nodes.append(filenode) + + ast = IDLNode('AST', '__AST__', 0, 0, nodes) + + print '\n'.join(ast.Tree(accept_props=['PROD'])) + if errors: + print '\nFound %d errors.\n' % errors + + return errors + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/tools/idl_parser/idl_parser_test.py b/engine/src/flutter/tools/idl_parser/idl_parser_test.py new file mode 100755 index 0000000000..76a9571b9d --- /dev/null +++ b/engine/src/flutter/tools/idl_parser/idl_parser_test.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# 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. + +import glob +import unittest + +from idl_lexer import IDLLexer +from idl_parser import IDLParser, ParseFile +from idl_ppapi_lexer import IDLPPAPILexer +from idl_ppapi_parser import IDLPPAPIParser + +def ParseCommentTest(comment): + comment = comment.strip() + comments = comment.split(None, 1) + return comments[0], comments[1] + + +class WebIDLParser(unittest.TestCase): + def setUp(self): + self.parser = IDLParser(IDLLexer(), mute_error=True) + self.filenames = glob.glob('test_parser/*_web.idl') + + def _TestNode(self, node): + comments = node.GetListOf('Comment') + for comment in comments: + check, value = ParseCommentTest(comment.GetName()) + if check == 'BUILD': + msg = 'Expecting %s, but found %s.\n' % (value, str(node)) + self.assertEqual(value, str(node), msg) + + if check == 'ERROR': + msg = node.GetLogLine('Expecting\n\t%s\nbut found \n\t%s\n' % ( + value, str(node))) + self.assertEqual(value, node.GetName(), msg) + + if check == 'PROP': + key, expect = value.split('=') + actual = str(node.GetProperty(key)) + msg = 'Mismatched property %s: %s vs %s.\n' % (key, expect, actual) + self.assertEqual(expect, actual, msg) + + if check == 'TREE': + quick = '\n'.join(node.Tree()) + lineno = node.GetProperty('LINENO') + msg = 'Mismatched tree at line %d:\n%sVS\n%s' % (lineno, value, quick) + self.assertEqual(value, quick, msg) + + def testExpectedNodes(self): + for filename in self.filenames: + filenode = ParseFile(self.parser, filename) + children = filenode.GetChildren() + self.assertTrue(len(children) > 2, 'Expecting children in %s.' % + filename) + + for node in filenode.GetChildren()[2:]: + self._TestNode(node) + + +class PepperIDLParser(unittest.TestCase): + def setUp(self): + self.parser = IDLPPAPIParser(IDLPPAPILexer(), mute_error=True) + self.filenames = glob.glob('test_parser/*_ppapi.idl') + + def _TestNode(self, filename, node): + comments = node.GetListOf('Comment') + for comment in comments: + check, value = ParseCommentTest(comment.GetName()) + if check == 'BUILD': + msg = '%s - Expecting %s, but found %s.\n' % ( + filename, value, str(node)) + self.assertEqual(value, str(node), msg) + + if check == 'ERROR': + msg = node.GetLogLine('%s - Expecting\n\t%s\nbut found \n\t%s\n' % ( + filename, value, str(node))) + self.assertEqual(value, node.GetName(), msg) + + if check == 'PROP': + key, expect = value.split('=') + actual = str(node.GetProperty(key)) + msg = '%s - Mismatched property %s: %s vs %s.\n' % ( + filename, key, expect, actual) + self.assertEqual(expect, actual, msg) + + if check == 'TREE': + quick = '\n'.join(node.Tree()) + lineno = node.GetProperty('LINENO') + msg = '%s - Mismatched tree at line %d:\n%sVS\n%s' % ( + filename, lineno, value, quick) + self.assertEqual(value, quick, msg) + + def testExpectedNodes(self): + for filename in self.filenames: + filenode = ParseFile(self.parser, filename) + children = filenode.GetChildren() + self.assertTrue(len(children) > 2, 'Expecting children in %s.' % + filename) + + for node in filenode.GetChildren()[2:]: + self._TestNode(filename, node) + +if __name__ == '__main__': + unittest.main(verbosity=2) + diff --git a/engine/src/flutter/tools/idl_parser/idl_ppapi_lexer.py b/engine/src/flutter/tools/idl_parser/idl_ppapi_lexer.py new file mode 100755 index 0000000000..ac6f42cc2a --- /dev/null +++ b/engine/src/flutter/tools/idl_parser/idl_ppapi_lexer.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# 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. + +""" Lexer for PPAPI IDL + +The lexer uses the PLY library to build a tokenizer which understands both +WebIDL and Pepper tokens. + +WebIDL, and WebIDL regular expressions can be found at: + http://www.w3.org/TR/2012/CR-WebIDL-20120419/ +PLY can be found at: + http://www.dabeaz.com/ply/ +""" + +from idl_lexer import IDLLexer + + +# +# IDL PPAPI Lexer +# +class IDLPPAPILexer(IDLLexer): + # Token definitions + # + # These need to be methods for lexer construction, despite not using self. + # pylint: disable=R0201 + + # Special multi-character operators + def t_LSHIFT(self, t): + r'<<' + return t + + def t_RSHIFT(self, t): + r'>>' + return t + + def t_INLINE(self, t): + r'\#inline (.|\n)*?\#endinl.*' + self.AddLines(t.value.count('\n')) + return t + + # Return a "preprocessor" inline block + def __init__(self): + IDLLexer.__init__(self) + self._AddTokens(['INLINE', 'LSHIFT', 'RSHIFT']) + self._AddKeywords(['label', 'struct']) + + # Add number types + self._AddKeywords(['char', 'int8_t', 'int16_t', 'int32_t', 'int64_t']) + self._AddKeywords(['uint8_t', 'uint16_t', 'uint32_t', 'uint64_t']) + self._AddKeywords(['double_t', 'float_t']) + + # Add handle types + self._AddKeywords(['handle_t', 'PP_FileHandle']) + + # Add pointer types (void*, char*, const char*, const void*) + self._AddKeywords(['mem_t', 'str_t', 'cstr_t', 'interface_t']) + + # Remove JS types + self._DelKeywords(['boolean', 'byte', 'ByteString', 'Date', 'DOMString', + 'double', 'float', 'long', 'object', 'octet', 'Promise', + 'RegExp', 'short', 'unsigned']) + + +# If run by itself, attempt to build the lexer +if __name__ == '__main__': + lexer = IDLPPAPILexer() + lexer.Tokenize(open('test_parser/inline_ppapi.idl').read()) + for tok in lexer.GetTokens(): + print '\n' + str(tok) diff --git a/engine/src/flutter/tools/idl_parser/idl_ppapi_parser.py b/engine/src/flutter/tools/idl_parser/idl_ppapi_parser.py new file mode 100755 index 0000000000..8914c84199 --- /dev/null +++ b/engine/src/flutter/tools/idl_parser/idl_ppapi_parser.py @@ -0,0 +1,317 @@ +#!/usr/bin/env python +# 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. + +""" Parser for PPAPI IDL """ + +# +# IDL Parser +# +# The parser is uses the PLY yacc library to build a set of parsing rules based +# on WebIDL. +# +# WebIDL, and WebIDL grammar can be found at: +# http://heycam.github.io/webidl/ +# PLY can be found at: +# http://www.dabeaz.com/ply/ +# +# The parser generates a tree by recursively matching sets of items against +# defined patterns. When a match is made, that set of items is reduced +# to a new item. The new item can provide a match for parent patterns. +# In this way an AST is built (reduced) depth first. +# + +# +# Disable check for line length and Member as Function due to how grammar rules +# are defined with PLY +# +# pylint: disable=R0201 +# pylint: disable=C0301 + +import sys + +from idl_ppapi_lexer import IDLPPAPILexer +from idl_parser import IDLParser, ListFromConcat, ParseFile +from idl_node import IDLNode + +class IDLPPAPIParser(IDLParser): +# +# We force all input files to start with two comments. The first comment is a +# Copyright notice followed by a file comment and finally by file level +# productions. +# + # [0] Insert a TOP definition for Copyright and Comments + def p_Top(self, p): + """Top : COMMENT COMMENT Definitions""" + Copyright = self.BuildComment('Copyright', p, 1) + Filedoc = self.BuildComment('Comment', p, 2) + p[0] = ListFromConcat(Copyright, Filedoc, p[3]) + +# +#The parser is based on the WebIDL standard. See: +# http://heycam.github.io/webidl/#idl-grammar +# + # [1] + def p_Definitions(self, p): + """Definitions : ExtendedAttributeList Definition Definitions + | """ + if len(p) > 1: + p[2].AddChildren(p[1]) + p[0] = ListFromConcat(p[2], p[3]) + + # [2] Add INLINE definition + def p_Definition(self, p): + """Definition : CallbackOrInterface + | Struct + | Partial + | Dictionary + | Exception + | Enum + | Typedef + | ImplementsStatement + | Label + | Inline""" + p[0] = p[1] + + def p_Inline(self, p): + """Inline : INLINE""" + words = p[1].split() + name = self.BuildAttribute('NAME', words[1]) + lines = p[1].split('\n') + value = self.BuildAttribute('VALUE', '\n'.join(lines[1:-1]) + '\n') + children = ListFromConcat(name, value) + p[0] = self.BuildProduction('Inline', p, 1, children) + +# +# Label +# +# A label is a special kind of enumeration which allows us to go from a +# set of version numbrs to releases +# + def p_Label(self, p): + """Label : LABEL identifier '{' LabelList '}' ';'""" + p[0] = self.BuildNamed('Label', p, 2, p[4]) + + def p_LabelList(self, p): + """LabelList : identifier '=' float LabelCont""" + val = self.BuildAttribute('VALUE', p[3]) + label = self.BuildNamed('LabelItem', p, 1, val) + p[0] = ListFromConcat(label, p[4]) + + def p_LabelCont(self, p): + """LabelCont : ',' LabelList + |""" + if len(p) > 1: + p[0] = p[2] + + def p_LabelContError(self, p): + """LabelCont : error LabelCont""" + p[0] = p[2] + + # [5.1] Add "struct" style interface + def p_Struct(self, p): + """Struct : STRUCT identifier Inheritance '{' StructMembers '}' ';'""" + p[0] = self.BuildNamed('Struct', p, 2, ListFromConcat(p[3], p[5])) + + def p_StructMembers(self, p): + """StructMembers : StructMember StructMembers + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[1], p[2]) + + def p_StructMember(self, p): + """StructMember : ExtendedAttributeList Type identifier ';'""" + p[0] = self.BuildNamed('Member', p, 3, ListFromConcat(p[1], p[2])) + + def p_Typedef(self, p): + """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'""" + p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3])) + + def p_TypedefFunc(self, p): + """Typedef : TYPEDEF ExtendedAttributeListNoComments ReturnType identifier '(' ArgumentList ')' ';'""" + args = self.BuildProduction('Arguments', p, 5, p[6]) + p[0] = self.BuildNamed('Callback', p, 4, ListFromConcat(p[2], p[3], args)) + + def p_ConstValue(self, p): + """ConstValue : integer + | integer LSHIFT integer + | integer RSHIFT integer""" + val = str(p[1]) + if len(p) > 2: + val = "%s %s %s" % (p[1], p[2], p[3]) + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), + self.BuildAttribute('VALUE', val)) + + def p_ConstValueStr(self, p): + """ConstValue : string""" + p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'string'), + self.BuildAttribute('VALUE', p[1])) + + # Boolean & Float Literals area already BuildAttributes + def p_ConstValueLiteral(self, p): + """ConstValue : FloatLiteral + | BooleanLiteral """ + p[0] = p[1] + + def p_EnumValueList(self, p): + """EnumValueList : EnumValue EnumValues""" + p[0] = ListFromConcat(p[1], p[2]) + + def p_EnumValues(self, p): + """EnumValues : ',' EnumValue EnumValues + |""" + if len(p) > 1: + p[0] = ListFromConcat(p[2], p[3]) + + def p_EnumValue(self, p): + """EnumValue : ExtendedAttributeList identifier + | ExtendedAttributeList identifier '=' ConstValue""" + p[0] = self.BuildNamed('EnumItem', p, 2, p[1]) + if len(p) > 3: + p[0].AddChildren(p[4]) + + # Omit PromiseType, as it is a JS type. + def p_NonAnyType(self, p): + """NonAnyType : PrimitiveType TypeSuffix + | identifier TypeSuffix + | SEQUENCE '<' Type '>' Null""" + IDLParser.p_NonAnyType(self, p) + + def p_PrimitiveType(self, p): + """PrimitiveType : IntegerType + | UnsignedIntegerType + | FloatType + | HandleType + | PointerType""" + if type(p[1]) == str: + p[0] = self.BuildNamed('PrimitiveType', p, 1) + else: + p[0] = p[1] + + def p_PointerType(self, p): + """PointerType : STR_T + | MEM_T + | CSTR_T + | INTERFACE_T + | NULL""" + p[0] = p[1] + + def p_HandleType(self, p): + """HandleType : HANDLE_T + | PP_FILEHANDLE""" + p[0] = p[1] + + def p_FloatType(self, p): + """FloatType : FLOAT_T + | DOUBLE_T""" + p[0] = p[1] + + def p_UnsignedIntegerType(self, p): + """UnsignedIntegerType : UINT8_T + | UINT16_T + | UINT32_T + | UINT64_T""" + p[0] = p[1] + + + def p_IntegerType(self, p): + """IntegerType : CHAR + | INT8_T + | INT16_T + | INT32_T + | INT64_T""" + p[0] = p[1] + + # These targets are no longer used + def p_OptionalLong(self, p): + """ """ + pass + + def p_UnrestrictedFloatType(self, p): + """ """ + pass + + def p_null(self, p): + """ """ + pass + + def p_PromiseType(self, p): + """ """ + pass + + def p_EnumValueListComma(self, p): + """ """ + pass + + def p_EnumValueListString(self, p): + """ """ + pass + + # We only support: + # [ identifier ] + # [ identifier ( ArgumentList )] + # [ identifier ( ValueList )] + # [ identifier = identifier ] + # [ identifier = ( IdentifierList )] + # [ identifier = ConstValue ] + # [ identifier = identifier ( ArgumentList )] + # [51] map directly to 74-77 + # [52-54, 56] are unsupported + def p_ExtendedAttribute(self, p): + """ExtendedAttribute : ExtendedAttributeNoArgs + | ExtendedAttributeArgList + | ExtendedAttributeValList + | ExtendedAttributeIdent + | ExtendedAttributeIdentList + | ExtendedAttributeIdentConst + | ExtendedAttributeNamedArgList""" + p[0] = p[1] + + def p_ExtendedAttributeValList(self, p): + """ExtendedAttributeValList : identifier '(' ValueList ')'""" + arguments = self.BuildProduction('Values', p, 2, p[3]) + p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments) + + def p_ValueList(self, p): + """ValueList : ConstValue ValueListCont""" + p[0] = ListFromConcat(p[1], p[2]) + + def p_ValueListCont(self, p): + """ValueListCont : ValueList + |""" + if len(p) > 1: + p[0] = p[1] + + def p_ExtendedAttributeIdentConst(self, p): + """ExtendedAttributeIdentConst : identifier '=' ConstValue""" + p[0] = self.BuildNamed('ExtAttribute', p, 1, p[3]) + + + def __init__(self, lexer, verbose=False, debug=False, mute_error=False): + IDLParser.__init__(self, lexer, verbose, debug, mute_error) + + +def main(argv): + nodes = [] + parser = IDLPPAPIParser(IDLPPAPILexer()) + errors = 0 + + for filename in argv: + filenode = ParseFile(parser, filename) + if filenode: + errors += filenode.GetProperty('ERRORS') + nodes.append(filenode) + + ast = IDLNode('AST', '__AST__', 0, 0, nodes) + + print '\n'.join(ast.Tree(accept_props=['PROD', 'TYPE', 'VALUE'])) + if errors: + print '\nFound %d errors.\n' % errors + + + return errors + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/engine/src/flutter/tools/idl_parser/run_tests.py b/engine/src/flutter/tools/idl_parser/run_tests.py new file mode 100755 index 0000000000..cf26759f7b --- /dev/null +++ b/engine/src/flutter/tools/idl_parser/run_tests.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# 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. + +import glob +import sys +import unittest + +if __name__ == '__main__': + suite = unittest.TestSuite() + for testname in glob.glob('*_test.py'): + print 'Adding Test: ' + testname + module = __import__(testname[:-3]) + suite.addTests(unittest.defaultTestLoader.loadTestsFromModule(module)) + result = unittest.TextTestRunner(verbosity=2).run(suite) + if result.wasSuccessful(): + sys.exit(0) + else: + sys.exit(1) diff --git a/engine/src/flutter/tools/licenses.py b/engine/src/flutter/tools/licenses.py new file mode 100755 index 0000000000..e4cdb8eb83 --- /dev/null +++ b/engine/src/flutter/tools/licenses.py @@ -0,0 +1,472 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Utility for checking and processing licensing information in third_party +directories. + +Usage: licenses.py + +Commands: + scan scan third_party directories, verifying that we have licensing info + credits generate about:credits on stdout + +(You can also import this as a module.) +""" + +import cgi +import os +import sys + +# Paths from the root of the tree to directories to skip. +PRUNE_PATHS = set([ + # Same module occurs in crypto/third_party/nss and net/third_party/nss, so + # skip this one. + os.path.join('third_party','nss'), + + # Placeholder directory only, not third-party code. + os.path.join('third_party','adobe'), + + # Build files only, not third-party code. + os.path.join('third_party','widevine'), + + # Only binaries, used during development. + os.path.join('third_party','valgrind'), + + # Used for development and test, not in the shipping product. + os.path.join('build','secondary'), + os.path.join('third_party','bison'), + os.path.join('third_party','blanketjs'), + os.path.join('third_party','gnu_binutils'), + os.path.join('third_party','gold'), + os.path.join('third_party','gperf'), + os.path.join('third_party','lighttpd'), + os.path.join('third_party','llvm'), + os.path.join('third_party','llvm-build'), + os.path.join('third_party','nacl_sdk_binaries'), + os.path.join('third_party','pefile'), + os.path.join('third_party','perl'), + os.path.join('third_party','pylib'), + os.path.join('third_party','pywebsocket'), + os.path.join('third_party','qunit'), + os.path.join('third_party','sinonjs'), + os.path.join('third_party','syzygy'), + os.path.join('tools', 'profile_chrome', 'third_party'), + + # Chromium code in third_party. + os.path.join('third_party','fuzzymatch'), + os.path.join('tools', 'swarming_client'), + + # Stuff pulled in from chrome-internal for official builds/tools. + os.path.join('third_party', 'clear_cache'), + os.path.join('third_party', 'gnu'), + os.path.join('third_party', 'googlemac'), + os.path.join('third_party', 'pcre'), + os.path.join('third_party', 'psutils'), + os.path.join('third_party', 'sawbuck'), +]) + +# Directories we don't scan through. +VCS_METADATA_DIRS = ('.svn', '.git') +PRUNE_DIRS = (VCS_METADATA_DIRS + + ('out', 'Debug', 'Release', # build files + 'tests')) # lots of subdirs, not shipped. + +ADDITIONAL_PATHS = ( + os.path.join('breakpad'), + os.path.join('chrome', 'common', 'extensions', 'docs', 'examples'), + os.path.join('chrome', 'test', 'chromeos', 'autotest'), + os.path.join('chrome', 'test', 'data'), + os.path.join('native_client'), + os.path.join('net', 'tools', 'spdyshark'), + os.path.join('sdch', 'open-vcdiff'), + os.path.join('testing', 'gmock'), + os.path.join('testing', 'gtest'), + os.path.join('tools', 'grit'), + os.path.join('tools', 'gyp'), + os.path.join('tools', 'page_cycler', 'acid3'), + os.path.join('url', 'third_party', 'mozilla'), + os.path.join('v8'), + # Fake directory so we can include the strongtalk license. + os.path.join('v8', 'strongtalk'), + os.path.join('v8', 'third_party', 'fdlibm'), +) + + +# Directories where we check out directly from upstream, and therefore +# can't provide a README.chromium. Please prefer a README.chromium +# wherever possible. +SPECIAL_CASES = { + os.path.join('native_client'): { + "Name": "native client", + "URL": "http://code.google.com/p/nativeclient", + "License": "BSD", + }, + os.path.join('sdch', 'open-vcdiff'): { + "Name": "open-vcdiff", + "URL": "http://code.google.com/p/open-vcdiff", + "License": "Apache 2.0, MIT, GPL v2 and custom licenses", + "License Android Compatible": "yes", + }, + os.path.join('testing', 'gmock'): { + "Name": "gmock", + "URL": "http://code.google.com/p/googlemock", + "License": "BSD", + "License File": "NOT_SHIPPED", + }, + os.path.join('testing', 'gtest'): { + "Name": "gtest", + "URL": "http://code.google.com/p/googletest", + "License": "BSD", + "License File": "NOT_SHIPPED", + }, + os.path.join('third_party', 'angle'): { + "Name": "Almost Native Graphics Layer Engine", + "URL": "http://code.google.com/p/angleproject/", + "License": "BSD", + }, + os.path.join('third_party', 'cros_system_api'): { + "Name": "Chromium OS system API", + "URL": "http://www.chromium.org/chromium-os", + "License": "BSD", + # Absolute path here is resolved as relative to the source root. + "License File": "/LICENSE.chromium_os", + }, + os.path.join('third_party', 'lss'): { + "Name": "linux-syscall-support", + "URL": "http://code.google.com/p/linux-syscall-support/", + "License": "BSD", + "License File": "/LICENSE", + }, + os.path.join('third_party', 'ots'): { + "Name": "OTS (OpenType Sanitizer)", + "URL": "http://code.google.com/p/ots/", + "License": "BSD", + }, + os.path.join('third_party', 'pdfium'): { + "Name": "PDFium", + "URL": "http://code.google.com/p/pdfium/", + "License": "BSD", + }, + os.path.join('third_party', 'pdfsqueeze'): { + "Name": "pdfsqueeze", + "URL": "http://code.google.com/p/pdfsqueeze/", + "License": "Apache 2.0", + "License File": "COPYING", + }, + os.path.join('third_party', 'ppapi'): { + "Name": "ppapi", + "URL": "http://code.google.com/p/ppapi/", + }, + os.path.join('third_party', 'scons-2.0.1'): { + "Name": "scons-2.0.1", + "URL": "http://www.scons.org", + "License": "MIT", + "License File": "NOT_SHIPPED", + }, + os.path.join('third_party', 'trace-viewer'): { + "Name": "trace-viewer", + "URL": "http://code.google.com/p/trace-viewer", + "License": "BSD", + "License File": "NOT_SHIPPED", + }, + os.path.join('third_party', 'v8-i18n'): { + "Name": "Internationalization Library for v8", + "URL": "http://code.google.com/p/v8-i18n/", + "License": "Apache 2.0", + }, + os.path.join('third_party', 'WebKit'): { + "Name": "WebKit", + "URL": "http://webkit.org/", + "License": "BSD and GPL v2", + # Absolute path here is resolved as relative to the source root. + "License File": "/webkit/LICENSE", + }, + os.path.join('third_party', 'webpagereplay'): { + "Name": "webpagereplay", + "URL": "http://code.google.com/p/web-page-replay", + "License": "Apache 2.0", + "License File": "NOT_SHIPPED", + }, + os.path.join('tools', 'grit'): { + "Name": "grit", + "URL": "http://code.google.com/p/grit-i18n", + "License": "BSD", + "License File": "NOT_SHIPPED", + }, + os.path.join('tools', 'gyp'): { + "Name": "gyp", + "URL": "http://code.google.com/p/gyp", + "License": "BSD", + "License File": "NOT_SHIPPED", + }, + os.path.join('v8'): { + "Name": "V8 JavaScript Engine", + "URL": "http://code.google.com/p/v8", + "License": "BSD", + }, + os.path.join('v8', 'strongtalk'): { + "Name": "Strongtalk", + "URL": "http://www.strongtalk.org/", + "License": "BSD", + # Absolute path here is resolved as relative to the source root. + "License File": "/v8/LICENSE.strongtalk", + }, + os.path.join('v8', 'third_party', 'fdlibm'): { + "Name": "fdlibm", + "URL": "http://www.netlib.org/fdlibm/", + "License": "Freely Distributable", + # Absolute path here is resolved as relative to the source root. + "License File" : "/v8/third_party/fdlibm/LICENSE", + "License Android Compatible" : "yes", + }, + os.path.join('third_party', 'khronos_glcts'): { + # These sources are not shipped, are not public, and it isn't + # clear why they're tripping the license check. + "Name": "khronos_glcts", + "URL": "http://no-public-url", + "License": "Khronos", + "License File": "NOT_SHIPPED", + }, + os.path.join('tools', 'telemetry', 'third_party', 'gsutil'): { + "Name": "gsutil", + "URL": "https://cloud.google.com/storage/docs/gsutil", + "License": "Apache 2.0", + "License File": "NOT_SHIPPED", + }, +} + +# Special value for 'License File' field used to indicate that the license file +# should not be used in about:credits. +NOT_SHIPPED = "NOT_SHIPPED" + + +class LicenseError(Exception): + """We raise this exception when a directory's licensing info isn't + fully filled out.""" + pass + +def AbsolutePath(path, filename, root): + """Convert a path in README.chromium to be absolute based on the source + root.""" + if filename.startswith('/'): + # Absolute-looking paths are relative to the source root + # (which is the directory we're run from). + absolute_path = os.path.join(root, filename[1:]) + else: + absolute_path = os.path.join(root, path, filename) + if os.path.exists(absolute_path): + return absolute_path + return None + +def ParseDir(path, root, require_license_file=True, optional_keys=None): + """Examine a third_party/foo component and extract its metadata.""" + + # Parse metadata fields out of README.chromium. + # We examine "LICENSE" for the license file by default. + metadata = { + "License File": "LICENSE", # Relative path to license text. + "Name": None, # Short name (for header on about:credits). + "URL": None, # Project home page. + "License": None, # Software license. + } + + if optional_keys is None: + optional_keys = [] + + if path in SPECIAL_CASES: + metadata.update(SPECIAL_CASES[path]) + else: + # Try to find README.chromium. + readme_path = os.path.join(root, path, 'README.chromium') + if not os.path.exists(readme_path): + raise LicenseError("missing README.chromium or licenses.py " + "SPECIAL_CASES entry") + + for line in open(readme_path): + line = line.strip() + if not line: + break + for key in metadata.keys() + optional_keys: + field = key + ": " + if line.startswith(field): + metadata[key] = line[len(field):] + + # Check that all expected metadata is present. + for key, value in metadata.iteritems(): + if not value: + raise LicenseError("couldn't find '" + key + "' line " + "in README.chromium or licences.py " + "SPECIAL_CASES") + + # Special-case modules that aren't in the shipping product, so don't need + # their license in about:credits. + if metadata["License File"] != NOT_SHIPPED: + # Check that the license file exists. + for filename in (metadata["License File"], "COPYING"): + license_path = AbsolutePath(path, filename, root) + if license_path is not None: + break + + if require_license_file and not license_path: + raise LicenseError("License file not found. " + "Either add a file named LICENSE, " + "import upstream's COPYING if available, " + "or add a 'License File:' line to " + "README.chromium with the appropriate path.") + metadata["License File"] = license_path + + return metadata + + +def ContainsFiles(path, root): + """Determines whether any files exist in a directory or in any of its + subdirectories.""" + for _, dirs, files in os.walk(os.path.join(root, path)): + if files: + return True + for vcs_metadata in VCS_METADATA_DIRS: + if vcs_metadata in dirs: + dirs.remove(vcs_metadata) + return False + + +def FilterDirsWithFiles(dirs_list, root): + # If a directory contains no files, assume it's a DEPS directory for a + # project not used by our current configuration and skip it. + return [x for x in dirs_list if ContainsFiles(x, root)] + + +def FindThirdPartyDirs(prune_paths, root): + """Find all third_party directories underneath the source root.""" + third_party_dirs = set() + for path, dirs, files in os.walk(root): + path = path[len(root)+1:] # Pretty up the path. + + if path in prune_paths: + dirs[:] = [] + continue + + # Prune out directories we want to skip. + # (Note that we loop over PRUNE_DIRS so we're not iterating over a + # list that we're simultaneously mutating.) + for skip in PRUNE_DIRS: + if skip in dirs: + dirs.remove(skip) + + if os.path.basename(path) == 'third_party': + # Add all subdirectories that are not marked for skipping. + for dir in dirs: + dirpath = os.path.join(path, dir) + if dirpath not in prune_paths: + third_party_dirs.add(dirpath) + + # Don't recurse into any subdirs from here. + dirs[:] = [] + continue + + # Don't recurse into paths in ADDITIONAL_PATHS, like we do with regular + # third_party/foo paths. + if path in ADDITIONAL_PATHS: + dirs[:] = [] + + for dir in ADDITIONAL_PATHS: + if dir not in prune_paths: + third_party_dirs.add(dir) + + return third_party_dirs + + +def ScanThirdPartyDirs(root=None): + """Scan a list of directories and report on any problems we find.""" + if root is None: + root = os.getcwd() + third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, root) + third_party_dirs = FilterDirsWithFiles(third_party_dirs, root) + + errors = [] + for path in sorted(third_party_dirs): + try: + metadata = ParseDir(path, root) + except LicenseError, e: + errors.append((path, e.args[0])) + continue + + for path, error in sorted(errors): + print path + ": " + error + + return len(errors) == 0 + + +def GenerateCredits(): + """Generate about:credits.""" + + if len(sys.argv) not in (2, 3): + print 'usage: licenses.py credits [output_file]' + return False + + def EvaluateTemplate(template, env, escape=True): + """Expand a template with variables like {{foo}} using a + dictionary of expansions.""" + for key, val in env.items(): + if escape: + val = cgi.escape(val) + template = template.replace('{{%s}}' % key, val) + return template + + root = os.path.join(os.path.dirname(__file__), '..') + third_party_dirs = FindThirdPartyDirs(PRUNE_PATHS, root) + templates_dir = os.path.join(os.path.dirname(__file__), 'resources') + + entry_template = open(os.path.join(templates_dir, + 'about_credits_entry.tmpl'), 'rb').read() + entries = [] + for path in sorted(third_party_dirs): + try: + metadata = ParseDir(path, root) + except LicenseError: + # TODO(phajdan.jr): Convert to fatal error (http://crbug.com/39240). + continue + if metadata['License File'] == NOT_SHIPPED: + continue + env = { + 'name': metadata['Name'], + 'url': metadata['URL'], + 'license': open(metadata['License File'], 'rb').read(), + } + entries.append(EvaluateTemplate(entry_template, env)) + + file_template = open(os.path.join(templates_dir, + 'about_credits.tmpl'), 'rb').read() + template_contents = EvaluateTemplate(file_template, + {'entries': '\n'.join(entries)}, + escape=False) + + if len(sys.argv) == 3: + with open(sys.argv[2], 'w') as output_file: + output_file.write(template_contents) + elif len(sys.argv) == 2: + print template_contents + + return True + + +def main(): + command = 'help' + if len(sys.argv) > 1: + command = sys.argv[1] + + if command == 'scan': + if not ScanThirdPartyDirs(): + return 1 + elif command == 'credits': + if not GenerateCredits(): + return 1 + else: + print __doc__ + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/linux/PRESUBMIT.py b/engine/src/flutter/tools/linux/PRESUBMIT.py new file mode 100644 index 0000000000..b29a8c4777 --- /dev/null +++ b/engine/src/flutter/tools/linux/PRESUBMIT.py @@ -0,0 +1,45 @@ +# Copyright 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. + +"""Top-level presubmit script for linux. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details on the presubmit API built into depot_tools. +""" + + +def CommonChecks(input_api, output_api): + import sys + def join(*args): + return input_api.os_path.join(input_api.PresubmitLocalPath(), *args) + + output = [] + sys_path_backup = sys.path + try: + sys.path = [ + join('..', 'linux'), + ] + sys.path + output.extend(input_api.canned_checks.RunPylint(input_api, output_api)) + finally: + sys.path = sys_path_backup + + output.extend( + input_api.canned_checks.RunUnitTestsInDirectory( + input_api, output_api, + input_api.os_path.join(input_api.PresubmitLocalPath(), 'tests'), + whitelist=[r'.+_tests\.py$'])) + + if input_api.is_committing: + output.extend(input_api.canned_checks.PanProjectChecks(input_api, + output_api, + owners_check=False)) + return output + + +def CheckChangeOnUpload(input_api, output_api): + return CommonChecks(input_api, output_api) + + +def CheckChangeOnCommit(input_api, output_api): + return CommonChecks(input_api, output_api) diff --git a/engine/src/flutter/tools/linux/dump-static-initializers.py b/engine/src/flutter/tools/linux/dump-static-initializers.py new file mode 100755 index 0000000000..0e83456d78 --- /dev/null +++ b/engine/src/flutter/tools/linux/dump-static-initializers.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Dump functions called by static intializers in a Linux Release binary. + +Usage example: + tools/linux/dump-static-intializers.py out/Release/chrome + +A brief overview of static initialization: +1) the compiler writes out, per object file, a function that contains + the static intializers for that file. +2) the compiler also writes out a pointer to that function in a special + section. +3) at link time, the linker concatenates the function pointer sections + into a single list of all initializers. +4) at run time, on startup the binary runs all function pointers. + +The functions in (1) all have mangled names of the form + _GLOBAL__I_foobar.cc +using objdump, we can disassemble those functions and dump all symbols that +they reference. +""" + +import optparse +import re +import subprocess +import sys + +# A map of symbol => informative text about it. +NOTES = { + '__cxa_atexit@plt': 'registers a dtor to run at exit', + 'std::__ioinit': '#includes , use instead', +} + +# Determine whether this is a git checkout (as opposed to e.g. svn). +IS_GIT_WORKSPACE = (subprocess.Popen( + ['git', 'rev-parse'], stderr=subprocess.PIPE).wait() == 0) + +class Demangler(object): + """A wrapper around c++filt to provide a function to demangle symbols.""" + def __init__(self): + self.cppfilt = subprocess.Popen(['c++filt'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + + def Demangle(self, sym): + """Given mangled symbol |sym|, return its demangled form.""" + self.cppfilt.stdin.write(sym + '\n') + return self.cppfilt.stdout.readline().strip() + +# Matches for example: "cert_logger.pb.cc", capturing "cert_logger". +protobuf_filename_re = re.compile(r'(.*)\.pb\.cc$') +def QualifyFilenameAsProto(filename): + """Attempt to qualify a bare |filename| with a src-relative path, assuming it + is a protoc-generated file. If a single match is found, it is returned. + Otherwise the original filename is returned.""" + if not IS_GIT_WORKSPACE: + return filename + match = protobuf_filename_re.match(filename) + if not match: + return filename + basename = match.groups(0) + gitlsfiles = subprocess.Popen( + ['git', 'ls-files', '--', '*/%s.proto' % basename], + stdout=subprocess.PIPE) + candidate = filename + for line in gitlsfiles.stdout: + if candidate != filename: + return filename # Multiple hits, can't help. + candidate = line.strip() + return candidate + +# Regex matching the substring of a symbol's demangled text representation most +# likely to appear in a source file. +# Example: "v8::internal::Builtins::InitBuiltinFunctionTable()" becomes +# "InitBuiltinFunctionTable", since the first (optional & non-capturing) group +# picks up any ::-qualification and the last fragment picks up a suffix that +# starts with an opener. +symbol_code_name_re = re.compile(r'^(?:[^(<[]*::)?([^:(<[]*).*?$') +def QualifyFilename(filename, symbol): + """Given a bare filename and a symbol that occurs in it, attempt to qualify + it with a src-relative path. If more than one file matches, return the + original filename.""" + if not IS_GIT_WORKSPACE: + return filename + match = symbol_code_name_re.match(symbol) + if not match: + return filename + symbol = match.group(1) + gitgrep = subprocess.Popen( + ['git', 'grep', '-l', symbol, '--', '*/%s' % filename], + stdout=subprocess.PIPE) + candidate = filename + for line in gitgrep.stdout: + if candidate != filename: # More than one candidate; return bare filename. + return filename + candidate = line.strip() + return candidate + +# Regex matching nm output for the symbols we're interested in. +# See test_ParseNmLine for examples. +nm_re = re.compile(r'(\S+) (\S+) t (?:_ZN12)?_GLOBAL__(?:sub_)?I_(.*)') +def ParseNmLine(line): + """Given a line of nm output, parse static initializers as a + (file, start, size) tuple.""" + match = nm_re.match(line) + if match: + addr, size, filename = match.groups() + return (filename, int(addr, 16), int(size, 16)) + + +def test_ParseNmLine(): + """Verify the nm_re regex matches some sample lines.""" + parse = ParseNmLine( + '0000000001919920 0000000000000008 t ' + '_ZN12_GLOBAL__I_safe_browsing_service.cc') + assert parse == ('safe_browsing_service.cc', 26319136, 8), parse + + parse = ParseNmLine( + '00000000026b9eb0 0000000000000024 t ' + '_GLOBAL__sub_I_extension_specifics.pb.cc') + assert parse == ('extension_specifics.pb.cc', 40607408, 36), parse + +# Just always run the test; it is fast enough. +test_ParseNmLine() + + +def ParseNm(binary): + """Given a binary, yield static initializers as (file, start, size) tuples.""" + nm = subprocess.Popen(['nm', '-S', binary], stdout=subprocess.PIPE) + for line in nm.stdout: + parse = ParseNmLine(line) + if parse: + yield parse + +# Regex matching objdump output for the symbols we're interested in. +# Example line: +# 12354ab: (disassembly, including ) +disassembly_re = re.compile(r'^\s+[0-9a-f]+:.*<(\S+)>') +def ExtractSymbolReferences(binary, start, end): + """Given a span of addresses, returns symbol references from disassembly.""" + cmd = ['objdump', binary, '--disassemble', + '--start-address=0x%x' % start, '--stop-address=0x%x' % end] + objdump = subprocess.Popen(cmd, stdout=subprocess.PIPE) + + refs = set() + for line in objdump.stdout: + if '__static_initialization_and_destruction' in line: + raise RuntimeError, ('code mentions ' + '__static_initialization_and_destruction; ' + 'did you accidentally run this on a Debug binary?') + match = disassembly_re.search(line) + if match: + (ref,) = match.groups() + if ref.startswith('.LC') or ref.startswith('_DYNAMIC'): + # Ignore these, they are uninformative. + continue + if ref.startswith('_GLOBAL__I_'): + # Probably a relative jump within this function. + continue + refs.add(ref) + + return sorted(refs) + +def main(): + parser = optparse.OptionParser(usage='%prog [option] filename') + parser.add_option('-d', '--diffable', dest='diffable', + action='store_true', default=False, + help='Prints the filename on each line, for more easily ' + 'diff-able output. (Used by sizes.py)') + opts, args = parser.parse_args() + if len(args) != 1: + parser.error('missing filename argument') + return 1 + binary = args[0] + + demangler = Demangler() + file_count = 0 + initializer_count = 0 + + files = ParseNm(binary) + if opts.diffable: + files = sorted(files) + for filename, addr, size in files: + file_count += 1 + ref_output = [] + + qualified_filename = QualifyFilenameAsProto(filename) + + if size == 2: + # gcc generates a two-byte 'repz retq' initializer when there is a + # ctor even when the ctor is empty. This is fixed in gcc 4.6, but + # Android uses gcc 4.4. + ref_output.append('[empty ctor, but it still has cost on gcc <4.6]') + else: + for ref in ExtractSymbolReferences(binary, addr, addr+size): + initializer_count += 1 + + ref = demangler.Demangle(ref) + if qualified_filename == filename: + qualified_filename = QualifyFilename(filename, ref) + + note = '' + if ref in NOTES: + note = NOTES[ref] + elif ref.endswith('_2eproto()'): + note = 'protocol compiler bug: crbug.com/105626' + + if note: + ref_output.append('%s [%s]' % (ref, note)) + else: + ref_output.append(ref) + + if opts.diffable: + if ref_output: + print '\n'.join('# ' + qualified_filename + ' ' + r for r in ref_output) + else: + print '# %s: (empty initializer list)' % qualified_filename + else: + print '%s (initializer offset 0x%x size 0x%x)' % (qualified_filename, + addr, size) + print ''.join(' %s\n' % r for r in ref_output) + + if opts.diffable: + print '#', + print 'Found %d static initializers in %d files.' % (initializer_count, + file_count) + + return 0 + +if '__main__' == __name__: + sys.exit(main()) diff --git a/engine/src/flutter/tools/linux/procfs.py b/engine/src/flutter/tools/linux/procfs.py new file mode 100755 index 0000000000..ef19b25ea5 --- /dev/null +++ b/engine/src/flutter/tools/linux/procfs.py @@ -0,0 +1,747 @@ +#!/usr/bin/env python +# Copyright 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 Python library to read and store procfs (/proc) information on Linux. +# +# Each information storage class in this file stores original data as original +# as reasonablly possible. Translation is done when requested. It is to make it +# always possible to probe the original data. + + +import collections +import logging +import os +import re +import struct +import sys + + +class _NullHandler(logging.Handler): + def emit(self, record): + pass + + +_LOGGER = logging.getLogger('procfs') +_LOGGER.addHandler(_NullHandler()) + + +class ProcStat(object): + """Reads and stores information in /proc/pid/stat.""" + _PATTERN = re.compile(r'^' + '(?P-?[0-9]+) ' + '\((?P.+)\) ' + '(?P[RSDZTW]) ' + '(?P-?[0-9]+) ' + '(?P-?[0-9]+) ' + '(?P-?[0-9]+) ' + '(?P-?[0-9]+) ' + '(?P-?[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+)', re.IGNORECASE) + + def __init__(self, raw, pid, vsize, rss): + self._raw = raw + self._pid = pid + self._vsize = vsize + self._rss = rss + + @staticmethod + def load_file(stat_f): + raw = stat_f.readlines() + stat = ProcStat._PATTERN.match(raw[0]) + return ProcStat(raw, + stat.groupdict().get('PID'), + stat.groupdict().get('VSIZE'), + stat.groupdict().get('RSS')) + + @staticmethod + def load(pid): + try: + with open(os.path.join('/proc', str(pid), 'stat'), 'r') as stat_f: + return ProcStat.load_file(stat_f) + except IOError: + return None + + @property + def raw(self): + return self._raw + + @property + def pid(self): + return int(self._pid) + + @property + def vsize(self): + return int(self._vsize) + + @property + def rss(self): + return int(self._rss) + + +class ProcStatm(object): + """Reads and stores information in /proc/pid/statm.""" + _PATTERN = re.compile(r'^' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P[0-9]+) ' + '(?P
[0-9]+)', re.IGNORECASE) + + def __init__(self, raw, size, resident, share, text, lib, data, dt): + self._raw = raw + self._size = size + self._resident = resident + self._share = share + self._text = text + self._lib = lib + self._data = data + self._dt = dt + + @staticmethod + def load_file(statm_f): + try: + raw = statm_f.readlines() + except (IOError, OSError): + return None + statm = ProcStatm._PATTERN.match(raw[0]) + return ProcStatm(raw, + statm.groupdict().get('SIZE'), + statm.groupdict().get('RESIDENT'), + statm.groupdict().get('SHARE'), + statm.groupdict().get('TEXT'), + statm.groupdict().get('LIB'), + statm.groupdict().get('DATA'), + statm.groupdict().get('DT')) + + @staticmethod + def load(pid): + try: + with open(os.path.join('/proc', str(pid), 'statm'), 'r') as statm_f: + return ProcStatm.load_file(statm_f) + except (IOError, OSError): + return None + + @property + def raw(self): + return self._raw + + @property + def size(self): + return int(self._size) + + @property + def resident(self): + return int(self._resident) + + @property + def share(self): + return int(self._share) + + @property + def text(self): + return int(self._text) + + @property + def lib(self): + return int(self._lib) + + @property + def data(self): + return int(self._data) + + @property + def dt(self): + return int(self._dt) + + +class ProcStatus(object): + """Reads and stores information in /proc/pid/status.""" + _PATTERN = re.compile(r'^(?P[A-Za-z0-9_]+):\s+(?P.*)') + + def __init__(self, raw, dct): + self._raw = raw + self._pid = dct.get('Pid') + self._name = dct.get('Name') + self._vm_peak = dct.get('VmPeak') + self._vm_size = dct.get('VmSize') + self._vm_lck = dct.get('VmLck') + self._vm_pin = dct.get('VmPin') + self._vm_hwm = dct.get('VmHWM') + self._vm_rss = dct.get('VmRSS') + self._vm_data = dct.get('VmData') + self._vm_stack = dct.get('VmStk') + self._vm_exe = dct.get('VmExe') + self._vm_lib = dct.get('VmLib') + self._vm_pte = dct.get('VmPTE') + self._vm_swap = dct.get('VmSwap') + + @staticmethod + def load_file(status_f): + raw = status_f.readlines() + dct = {} + for line in raw: + status_match = ProcStatus._PATTERN.match(line) + if status_match: + match_dict = status_match.groupdict() + dct[match_dict['NAME']] = match_dict['VALUE'] + else: + raise SyntaxError('Unknown /proc/pid/status format.') + return ProcStatus(raw, dct) + + @staticmethod + def load(pid): + with open(os.path.join('/proc', str(pid), 'status'), 'r') as status_f: + return ProcStatus.load_file(status_f) + + @property + def raw(self): + return self._raw + + @property + def pid(self): + return int(self._pid) + + @property + def vm_peak(self): + """Returns a high-water (peak) virtual memory size in kilo-bytes.""" + if self._vm_peak.endswith('kB'): + return int(self._vm_peak.split()[0]) + raise ValueError('VmPeak is not in kB.') + + @property + def vm_size(self): + """Returns a virtual memory size in kilo-bytes.""" + if self._vm_size.endswith('kB'): + return int(self._vm_size.split()[0]) + raise ValueError('VmSize is not in kB.') + + @property + def vm_hwm(self): + """Returns a high-water (peak) resident set size (RSS) in kilo-bytes.""" + if self._vm_hwm.endswith('kB'): + return int(self._vm_hwm.split()[0]) + raise ValueError('VmHWM is not in kB.') + + @property + def vm_rss(self): + """Returns a resident set size (RSS) in kilo-bytes.""" + if self._vm_rss.endswith('kB'): + return int(self._vm_rss.split()[0]) + raise ValueError('VmRSS is not in kB.') + + +class ProcMapsEntry(object): + """A class representing one line in /proc/pid/maps.""" + + def __init__( + self, begin, end, readable, writable, executable, private, offset, + major, minor, inode, name): + self.begin = begin + self.end = end + self.readable = readable + self.writable = writable + self.executable = executable + self.private = private + self.offset = offset + self.major = major + self.minor = minor + self.inode = inode + self.name = name + + def as_dict(self): + return { + 'begin': self.begin, + 'end': self.end, + 'readable': self.readable, + 'writable': self.writable, + 'executable': self.executable, + 'private': self.private, + 'offset': self.offset, + 'major': self.major, + 'minor': self.minor, + 'inode': self.inode, + 'name': self.name, + } + + +class ProcMaps(object): + """Reads and stores information in /proc/pid/maps.""" + + MAPS_PATTERN = re.compile( + r'^([a-f0-9]+)-([a-f0-9]+)\s+(.)(.)(.)(.)\s+([a-f0-9]+)\s+(\S+):(\S+)\s+' + r'(\d+)\s*(.*)$', re.IGNORECASE) + + EXECUTABLE_PATTERN = re.compile( + r'\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?') + + def __init__(self): + self._sorted_indexes = [] + self._dictionary = {} + self._sorted = True + + def iter(self, condition): + if not self._sorted: + self._sorted_indexes.sort() + self._sorted = True + for index in self._sorted_indexes: + if not condition or condition(self._dictionary[index]): + yield self._dictionary[index] + + def __iter__(self): + if not self._sorted: + self._sorted_indexes.sort() + self._sorted = True + for index in self._sorted_indexes: + yield self._dictionary[index] + + @staticmethod + def load_file(maps_f): + table = ProcMaps() + for line in maps_f: + table.append_line(line) + return table + + @staticmethod + def load(pid): + try: + with open(os.path.join('/proc', str(pid), 'maps'), 'r') as maps_f: + return ProcMaps.load_file(maps_f) + except (IOError, OSError): + return None + + def append_line(self, line): + entry = self.parse_line(line) + if entry: + self._append_entry(entry) + return entry + + @staticmethod + def parse_line(line): + matched = ProcMaps.MAPS_PATTERN.match(line) + if matched: + return ProcMapsEntry( # pylint: disable=W0212 + int(matched.group(1), 16), # begin + int(matched.group(2), 16), # end + matched.group(3), # readable + matched.group(4), # writable + matched.group(5), # executable + matched.group(6), # private + int(matched.group(7), 16), # offset + matched.group(8), # major + matched.group(9), # minor + int(matched.group(10), 10), # inode + matched.group(11) # name + ) + else: + return None + + @staticmethod + def constants(entry): + return entry.writable == '-' and entry.executable == '-' + + @staticmethod + def executable(entry): + return entry.executable == 'x' + + @staticmethod + def executable_and_constants(entry): + return ((entry.writable == '-' and entry.executable == '-') or + entry.executable == 'x') + + def _append_entry(self, entry): + if self._sorted_indexes and self._sorted_indexes[-1] > entry.begin: + self._sorted = False + self._sorted_indexes.append(entry.begin) + self._dictionary[entry.begin] = entry + + +class ProcSmaps(object): + """Reads and stores information in /proc/pid/smaps.""" + _SMAPS_PATTERN = re.compile(r'^(?P[A-Za-z0-9_]+):\s+(?P.*)') + + class VMA(object): + def __init__(self): + self._size = 0 + self._rss = 0 + self._pss = 0 + + def append(self, name, value): + dct = { + 'Size': '_size', + 'Rss': '_rss', + 'Pss': '_pss', + 'Referenced': '_referenced', + 'Private_Clean': '_private_clean', + 'Shared_Clean': '_shared_clean', + 'KernelPageSize': '_kernel_page_size', + 'MMUPageSize': '_mmu_page_size', + } + if name in dct: + self.__setattr__(dct[name], value) + + @property + def size(self): + if self._size.endswith('kB'): + return int(self._size.split()[0]) + return int(self._size) + + @property + def rss(self): + if self._rss.endswith('kB'): + return int(self._rss.split()[0]) + return int(self._rss) + + @property + def pss(self): + if self._pss.endswith('kB'): + return int(self._pss.split()[0]) + return int(self._pss) + + def __init__(self, raw, total_dct, maps, vma_internals): + self._raw = raw + self._size = total_dct['Size'] + self._rss = total_dct['Rss'] + self._pss = total_dct['Pss'] + self._referenced = total_dct['Referenced'] + self._shared_clean = total_dct['Shared_Clean'] + self._private_clean = total_dct['Private_Clean'] + self._kernel_page_size = total_dct['KernelPageSize'] + self._mmu_page_size = total_dct['MMUPageSize'] + self._maps = maps + self._vma_internals = vma_internals + + @staticmethod + def load(pid): + with open(os.path.join('/proc', str(pid), 'smaps'), 'r') as smaps_f: + raw = smaps_f.readlines() + + vma = None + vma_internals = collections.OrderedDict() + total_dct = collections.defaultdict(int) + maps = ProcMaps() + for line in raw: + maps_match = ProcMaps.MAPS_PATTERN.match(line) + if maps_match: + vma = maps.append_line(line.strip()) + vma_internals[vma] = ProcSmaps.VMA() + else: + smaps_match = ProcSmaps._SMAPS_PATTERN.match(line) + if smaps_match: + match_dict = smaps_match.groupdict() + vma_internals[vma].append(match_dict['NAME'], match_dict['VALUE']) + total_dct[match_dict['NAME']] += int(match_dict['VALUE'].split()[0]) + + return ProcSmaps(raw, total_dct, maps, vma_internals) + + @property + def size(self): + return self._size + + @property + def rss(self): + return self._rss + + @property + def referenced(self): + return self._referenced + + @property + def pss(self): + return self._pss + + @property + def private_clean(self): + return self._private_clean + + @property + def shared_clean(self): + return self._shared_clean + + @property + def kernel_page_size(self): + return self._kernel_page_size + + @property + def mmu_page_size(self): + return self._mmu_page_size + + @property + def vma_internals(self): + return self._vma_internals + + +class ProcPagemap(object): + """Reads and stores partial information in /proc/pid/pagemap. + + It picks up virtual addresses to read based on ProcMaps (/proc/pid/maps). + See https://www.kernel.org/doc/Documentation/vm/pagemap.txt for details. + """ + _BYTES_PER_PAGEMAP_VALUE = 8 + _BYTES_PER_OS_PAGE = 4096 + _VIRTUAL_TO_PAGEMAP_OFFSET = _BYTES_PER_OS_PAGE / _BYTES_PER_PAGEMAP_VALUE + + _MASK_PRESENT = 1 << 63 + _MASK_SWAPPED = 1 << 62 + _MASK_FILEPAGE_OR_SHAREDANON = 1 << 61 + _MASK_SOFTDIRTY = 1 << 55 + _MASK_PFN = (1 << 55) - 1 + + class VMA(object): + def __init__(self, vsize, present, swapped, pageframes): + self._vsize = vsize + self._present = present + self._swapped = swapped + self._pageframes = pageframes + + @property + def vsize(self): + return int(self._vsize) + + @property + def present(self): + return int(self._present) + + @property + def swapped(self): + return int(self._swapped) + + @property + def pageframes(self): + return self._pageframes + + def __init__(self, vsize, present, swapped, vma_internals, in_process_dup): + self._vsize = vsize + self._present = present + self._swapped = swapped + self._vma_internals = vma_internals + self._in_process_dup = in_process_dup + + @staticmethod + def load(pid, maps): + total_present = 0 + total_swapped = 0 + total_vsize = 0 + in_process_dup = 0 + vma_internals = collections.OrderedDict() + process_pageframe_set = set() + + try: + pagemap_fd = os.open( + os.path.join('/proc', str(pid), 'pagemap'), os.O_RDONLY) + except (IOError, OSError): + return None + for vma in maps: + present = 0 + swapped = 0 + vsize = 0 + pageframes = collections.defaultdict(int) + begin_offset = ProcPagemap._offset(vma.begin) + chunk_size = ProcPagemap._offset(vma.end) - begin_offset + try: + os.lseek(pagemap_fd, begin_offset, os.SEEK_SET) + buf = os.read(pagemap_fd, chunk_size) + except (IOError, OSError): + return None + if len(buf) < chunk_size: + _LOGGER.warn('Failed to read pagemap at 0x%x in %d.' % (vma.begin, pid)) + pagemap_values = struct.unpack( + '=%dQ' % (len(buf) / ProcPagemap._BYTES_PER_PAGEMAP_VALUE), buf) + for pagemap_value in pagemap_values: + vsize += ProcPagemap._BYTES_PER_OS_PAGE + if pagemap_value & ProcPagemap._MASK_PRESENT: + if (pagemap_value & ProcPagemap._MASK_PFN) in process_pageframe_set: + in_process_dup += ProcPagemap._BYTES_PER_OS_PAGE + else: + process_pageframe_set.add(pagemap_value & ProcPagemap._MASK_PFN) + if (pagemap_value & ProcPagemap._MASK_PFN) not in pageframes: + present += ProcPagemap._BYTES_PER_OS_PAGE + pageframes[pagemap_value & ProcPagemap._MASK_PFN] += 1 + if pagemap_value & ProcPagemap._MASK_SWAPPED: + swapped += ProcPagemap._BYTES_PER_OS_PAGE + vma_internals[vma] = ProcPagemap.VMA(vsize, present, swapped, pageframes) + total_present += present + total_swapped += swapped + total_vsize += vsize + try: + os.close(pagemap_fd) + except OSError: + return None + + return ProcPagemap(total_vsize, total_present, total_swapped, + vma_internals, in_process_dup) + + @staticmethod + def _offset(virtual_address): + return virtual_address / ProcPagemap._VIRTUAL_TO_PAGEMAP_OFFSET + + @property + def vsize(self): + return int(self._vsize) + + @property + def present(self): + return int(self._present) + + @property + def swapped(self): + return int(self._swapped) + + @property + def vma_internals(self): + return self._vma_internals + + +class _ProcessMemory(object): + """Aggregates process memory information from /proc for manual testing.""" + def __init__(self, pid): + self._pid = pid + self._maps = None + self._pagemap = None + self._stat = None + self._status = None + self._statm = None + self._smaps = [] + + def _read(self, proc_file): + lines = [] + with open(os.path.join('/proc', str(self._pid), proc_file), 'r') as proc_f: + lines = proc_f.readlines() + return lines + + def read_all(self): + self.read_stat() + self.read_statm() + self.read_status() + self.read_smaps() + self.read_maps() + self.read_pagemap(self._maps) + + def read_maps(self): + self._maps = ProcMaps.load(self._pid) + + def read_pagemap(self, maps): + self._pagemap = ProcPagemap.load(self._pid, maps) + + def read_smaps(self): + self._smaps = ProcSmaps.load(self._pid) + + def read_stat(self): + self._stat = ProcStat.load(self._pid) + + def read_statm(self): + self._statm = ProcStatm.load(self._pid) + + def read_status(self): + self._status = ProcStatus.load(self._pid) + + @property + def pid(self): + return self._pid + + @property + def maps(self): + return self._maps + + @property + def pagemap(self): + return self._pagemap + + @property + def smaps(self): + return self._smaps + + @property + def stat(self): + return self._stat + + @property + def statm(self): + return self._statm + + @property + def status(self): + return self._status + + +def main(argv): + """The main function for manual testing.""" + _LOGGER.setLevel(logging.WARNING) + handler = logging.StreamHandler() + handler.setLevel(logging.WARNING) + handler.setFormatter(logging.Formatter( + '%(asctime)s:%(name)s:%(levelname)s:%(message)s')) + _LOGGER.addHandler(handler) + + pids = [] + for arg in argv[1:]: + try: + pid = int(arg) + except ValueError: + raise SyntaxError("%s is not an integer." % arg) + else: + pids.append(pid) + + procs = {} + for pid in pids: + procs[pid] = _ProcessMemory(pid) + procs[pid].read_all() + + print '=== PID: %d ===' % pid + + print ' stat: %d' % procs[pid].stat.vsize + print ' statm: %d' % (procs[pid].statm.size * 4096) + print ' status: %d (Peak:%d)' % (procs[pid].status.vm_size * 1024, + procs[pid].status.vm_peak * 1024) + print ' smaps: %d' % (procs[pid].smaps.size * 1024) + print 'pagemap: %d' % procs[pid].pagemap.vsize + print ' stat: %d' % (procs[pid].stat.rss * 4096) + print ' statm: %d' % (procs[pid].statm.resident * 4096) + print ' status: %d (Peak:%d)' % (procs[pid].status.vm_rss * 1024, + procs[pid].status.vm_hwm * 1024) + print ' smaps: %d' % (procs[pid].smaps.rss * 1024) + print 'pagemap: %d' % procs[pid].pagemap.present + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/tools/linux/tests/procfs_tests.py b/engine/src/flutter/tools/linux/tests/procfs_tests.py new file mode 100755 index 0000000000..c829199c60 --- /dev/null +++ b/engine/src/flutter/tools/linux/tests/procfs_tests.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# Copyright 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. + +import cStringIO +import logging +import os +import sys +import unittest + +ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, ROOT_DIR) + +from procfs import ProcMaps + + +class ProcMapsTest(unittest.TestCase): + _TEST_PROCMAPS = '\n'.join([ + '00000000-00001000 r--p 00000000 fc:00 0', + '0080b000-0080c000 r-xp 0020b000 fc:00 2231329' + ' /usr/bin/some', + '0080c000-0080f000 ---p 0020c000 fc:00 2231329' + ' /usr/bin/some', + '0100a000-0100c000 r-xp 0120a000 fc:00 22381' + ' /usr/bin/chrome', + '0100c000-0100f000 ---p 0120c000 fc:00 22381' + ' /usr/bin/chrome', + '0237d000-02a9b000 rw-p 00000000 00:00 0' + ' [heap]', + '7fb920e6d000-7fb920e85000 r-xp 00000000 fc:00 263482' + ' /lib/x86_64-linux-gnu/libpthread-2.15.so', + '7fb920e85000-7fb921084000 ---p 00018000 fc:00 263482' + ' /lib/x86_64-linux-gnu/libpthread-2.15.so', + '7fb9225f4000-7fb922654000 rw-s 00000000 00:04 19660808' + ' /SYSV00000000 (deleted)', + 'ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0' + ' [vsyscall]', + ]) + + _EXPECTED = [ + (0x0, 0x1000, 'r', '-', '-', 'p', 0x0, 'fc', '00', 0, ''), + (0x80b000, 0x80c000, 'r', '-', 'x', 'p', 0x20b000, + 'fc', '00', 2231329, '/usr/bin/some'), + (0x80c000, 0x80f000, '-', '-', '-', 'p', 0x20c000, + 'fc', '00', 2231329, '/usr/bin/some'), + (0x100a000, 0x100c000, 'r', '-', 'x', 'p', 0x120a000, + 'fc', '00', 22381, '/usr/bin/chrome'), + (0x100c000, 0x100f000, '-', '-', '-', 'p', 0x120c000, + 'fc', '00', 22381, '/usr/bin/chrome'), + (0x237d000, 0x2a9b000, 'r', 'w', '-', 'p', 0x0, + '00', '00', 0, '[heap]'), + (0x7fb920e6d000, 0x7fb920e85000, 'r', '-', 'x', 'p', 0x0, + 'fc', '00', 263482, '/lib/x86_64-linux-gnu/libpthread-2.15.so'), + (0x7fb920e85000, 0x7fb921084000, '-', '-', '-', 'p', 0x18000, + 'fc', '00', 263482, '/lib/x86_64-linux-gnu/libpthread-2.15.so'), + (0x7fb9225f4000, 0x7fb922654000, 'r', 'w', '-', 's', 0x0, + '00', '04', 19660808, '/SYSV00000000 (deleted)'), + (0xffffffffff600000, 0xffffffffff601000, 'r', '-', 'x', 'p', 0x0, + '00', '00', 0, '[vsyscall]'), + ] + + @staticmethod + def _expected_as_dict(index): + return { + 'begin': ProcMapsTest._EXPECTED[index][0], + 'end': ProcMapsTest._EXPECTED[index][1], + 'readable': ProcMapsTest._EXPECTED[index][2], + 'writable': ProcMapsTest._EXPECTED[index][3], + 'executable': ProcMapsTest._EXPECTED[index][4], + 'private': ProcMapsTest._EXPECTED[index][5], + 'offset': ProcMapsTest._EXPECTED[index][6], + 'major': ProcMapsTest._EXPECTED[index][7], + 'minor': ProcMapsTest._EXPECTED[index][8], + 'inode': ProcMapsTest._EXPECTED[index][9], + 'name': ProcMapsTest._EXPECTED[index][10], + } + + def test_load(self): + maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS)) + for index, entry in enumerate(maps): + self.assertEqual(entry.as_dict(), self._expected_as_dict(index)) + + def test_constants(self): + maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS)) + selected = [0, 2, 4, 7] + for index, entry in enumerate(maps.iter(ProcMaps.constants)): + self.assertEqual(entry.as_dict(), + self._expected_as_dict(selected[index])) + + def test_executable(self): + maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS)) + selected = [1, 3, 6, 9] + for index, entry in enumerate(maps.iter(ProcMaps.executable)): + self.assertEqual(entry.as_dict(), + self._expected_as_dict(selected[index])) + + def test_executable_and_constants(self): + maps = ProcMaps.load_file(cStringIO.StringIO(self._TEST_PROCMAPS)) + selected = [0, 1, 2, 3, 4, 6, 7, 9] + for index, entry in enumerate(maps.iter(ProcMaps.executable_and_constants)): + self.assertEqual(entry.as_dict(), + self._expected_as_dict(selected[index])) + + +if __name__ == '__main__': + logging.basicConfig( + level=logging.DEBUG if '-v' in sys.argv else logging.ERROR, + format='%(levelname)5s %(filename)15s(%(lineno)3d): %(message)s') + unittest.main() diff --git a/engine/src/flutter/tools/memory/asan/blacklist.txt b/engine/src/flutter/tools/memory/asan/blacklist.txt new file mode 100644 index 0000000000..99a147267e --- /dev/null +++ b/engine/src/flutter/tools/memory/asan/blacklist.txt @@ -0,0 +1,5 @@ +# The rules in this file are only applied at compile time. +# Because the Chrome buildsystem does not automatically touch the files +# mentioned here, changing this file requires clobbering all ASan bots. +# +# Please think twice before you add or remove these rules. diff --git a/engine/src/flutter/tools/protoc_wrapper/protoc_wrapper.py b/engine/src/flutter/tools/protoc_wrapper/protoc_wrapper.py new file mode 100755 index 0000000000..e6ddf95184 --- /dev/null +++ b/engine/src/flutter/tools/protoc_wrapper/protoc_wrapper.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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 simple wrapper for protoc. + +- Adds includes in generated headers. +- Handles building with system protobuf as an option. +""" + +import fnmatch +import optparse +import os.path +import shutil +import subprocess +import sys +import tempfile + +PROTOC_INCLUDE_POINT = '// @@protoc_insertion_point(includes)\n' + +def ModifyHeader(header_file, extra_header): + """Adds |extra_header| to |header_file|. Returns 0 on success. + + |extra_header| is the name of the header file to include. + |header_file| is a generated protobuf cpp header. + """ + include_point_found = False + header_contents = [] + with open(header_file) as f: + for line in f: + header_contents.append(line) + if line == PROTOC_INCLUDE_POINT: + extra_header_msg = '#include "%s"\n' % extra_header + header_contents.append(extra_header_msg) + include_point_found = True; + if not include_point_found: + return 1 + + with open(header_file, 'wb') as f: + f.write(''.join(header_contents)) + return 0 + +def ScanForBadFiles(scan_root): + """Scan for bad file names, see http://crbug.com/386125 for details. + Returns True if any filenames are bad. Outputs errors to stderr. + + |scan_root| is the path to the directory to be recursively scanned. + """ + badname = False + real_scan_root = os.path.realpath(scan_root) + for dirpath, dirnames, filenames in os.walk(real_scan_root): + matches = fnmatch.filter(filenames, '*-*.proto') + if len(matches) > 0: + if not badname: + badname = True + sys.stderr.write('proto files must not have hyphens in their names (' + 'see http://crbug.com/386125 for more information):\n') + for filename in matches: + sys.stderr.write(' ' + os.path.join(real_scan_root, + dirpath, filename) + '\n') + return badname + + +def RewriteProtoFilesForSystemProtobuf(path): + wrapper_dir = tempfile.mkdtemp() + try: + for filename in os.listdir(path): + if not filename.endswith('.proto'): + continue + with open(os.path.join(path, filename), 'r') as src_file: + with open(os.path.join(wrapper_dir, filename), 'w') as dst_file: + for line in src_file: + # Remove lines that break build with system protobuf. + # We cannot optimize for lite runtime, because system lite runtime + # does not have a Chromium-specific hack to retain unknown fields. + # Similarly, it does not understand corresponding option to control + # the usage of that hack. + if 'LITE_RUNTIME' in line or 'retain_unknown_fields' in line: + continue + dst_file.write(line) + + return wrapper_dir + except: + shutil.rmtree(wrapper_dir) + raise + + +def main(argv): + parser = optparse.OptionParser() + parser.add_option('--include', dest='extra_header', + help='The extra header to include. This must be specified ' + 'along with --protobuf.') + parser.add_option('--protobuf', dest='generated_header', + help='The c++ protobuf header to add the extra header to. ' + 'This must be specified along with --include.') + parser.add_option('--proto-in-dir', + help='The directory containing .proto files.') + parser.add_option('--proto-in-file', help='Input file to compile.') + parser.add_option('--use-system-protobuf', type=int, default=0, + help='Option to use system-installed protobuf ' + 'instead of bundled one.') + (options, args) = parser.parse_args(sys.argv) + if len(args) < 2: + return 1 + + if ScanForBadFiles(options.proto_in_dir): + return 1 + + proto_path = options.proto_in_dir + if options.use_system_protobuf == 1: + proto_path = RewriteProtoFilesForSystemProtobuf(proto_path) + try: + # Run what is hopefully protoc. + protoc_args = args[1:] + protoc_args += ['--proto_path=%s' % proto_path, + os.path.join(proto_path, options.proto_in_file)] + ret = subprocess.call(protoc_args) + if ret != 0: + return ret + finally: + if options.use_system_protobuf == 1: + # Remove temporary directory holding re-written files. + shutil.rmtree(proto_path) + + # protoc succeeded, check to see if the generated cpp header needs editing. + if not options.extra_header or not options.generated_header: + return 0 + return ModifyHeader(options.generated_header, options.extra_header) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/engine/src/flutter/tools/relocation_packer/BUILD.gn b/engine/src/flutter/tools/relocation_packer/BUILD.gn new file mode 100644 index 0000000000..563a64d62c --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/BUILD.gn @@ -0,0 +1,148 @@ +# Copyright 2014 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. + +import("config.gni") +import("//testing/test.gni") + +assert(relocation_packing_supported) + +if (target_cpu == "arm") { + target_define = "TARGET_ARM" +} else if (target_cpu == "arm64") { + target_define = "TARGET_ARM64" +} + +if (current_toolchain == host_toolchain) { + # GYP: //tools/relocation_packer/relocation_packer.gyp:lib_relocation_packer + source_set("lib_relocation_packer") { + defines = [ target_define ] + deps = [ + "//third_party/elfutils:libelf", + ] + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + sources = [ + "src/debug.cc", + "src/delta_encoder.cc", + "src/elf_file.cc", + "src/leb128.cc", + "src/packer.cc", + "src/run_length_encoder.cc", + "src/sleb128.cc", + ] + } + + # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer + executable("relocation_packer") { + defines = [ target_define ] + deps = [ + ":lib_relocation_packer", + "//third_party/elfutils:libelf", + ] + sources = [ + "src/main.cc", + ] + } + + # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_unittests + test("relocation_packer_unittests") { + sources = [ + "src/debug_unittest.cc", + "src/delta_encoder_unittest.cc", + "src/elf_file_unittest.cc", + "src/leb128_unittest.cc", + "src/packer_unittest.cc", + "src/run_all_unittests.cc", + "src/run_length_encoder_unittest.cc", + "src/sleb128_unittest.cc", + ] + rebased_test_data = rebase_path("test_data", root_build_dir) + data = [ + "test_data/elf_file_unittest_relocs_arm32.so", + "test_data/elf_file_unittest_relocs_arm32_packed.so", + "test_data/elf_file_unittest_relocs_arm64.so", + "test_data/elf_file_unittest_relocs_arm64_packed.so", + ] + defines = [ + target_define, + "INTERMEDIATE_DIR=\"$rebased_test_data\"", + ] + include_dirs = [ "//" ] + deps = [ + ":lib_relocation_packer", + ":relocation_packer_test_data", + "//testing:gtest", + ] + } +} + +if (current_toolchain == default_toolchain && + (target_cpu == "arm" || target_cpu == "arm64")) { + # Targets to build test data. These participate only in building test + # data for use with elf_file_unittest.cc, and are not part of the main + # relocation packer build. Unit test data files are checked in to the + # source tree as 'golden' data, and are not generated 'on the fly' by + # the build. + # + # See test_data/generate_elf_file_unittest_relocs.sh for instructions. + + # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_test_data + shared_library("relocation_packer_test_data") { + cflags = [ + "-O0", + "-g0", + ] + sources = [ + "test_data/elf_file_unittest_relocs.cc", + ] + } + + # GYP: //tools/relocation_packer/relocation_packer.gyp:relocation_packer_unittests_test_data + action("relocation_packer_unittests_test_data") { + script = "test_data/generate_elf_file_unittest_relocs.py" + test_file = "$root_build_dir/librelocation_packer_test_data.so" + if (target_cpu == "arm") { + added_section = ".android.rel.dyn" + packed_output = "elf_file_unittest_relocs_arm32_packed.so" + unpacked_output = "elf_file_unittest_relocs_arm32.so" + } else if (target_cpu == "arm64") { + added_section = ".android.rela.dyn" + packed_output = "elf_file_unittest_relocs_arm64_packed.so" + unpacked_output = "elf_file_unittest_relocs_arm64.so" + } else { + assert(false, "Unsupported target arch for relocation packer") + } + + packed_output = "$root_build_dir/$packed_output" + unpacked_output = "$root_build_dir/$unpacked_output" + + inputs = [ + test_file, + ] + + deps = [ + ":relocation_packer_test_data", + ":relocation_packer($host_toolchain)", + ] + + outputs = [ + packed_output, + unpacked_output, + ] + + args = [ + "--android-pack-relocations", + rebase_path(relocation_packer_exe, root_build_dir), + "--android-objcopy", + rebase_path(android_objcopy, root_build_dir), + "--added-section=$added_section", + "--test-file", + rebase_path(test_file, root_build_dir), + "--packed-output", + rebase_path(packed_output, root_build_dir), + "--unpacked-output", + rebase_path(unpacked_output, root_build_dir), + ] + } +} diff --git a/engine/src/flutter/tools/relocation_packer/LICENSE b/engine/src/flutter/tools/relocation_packer/LICENSE new file mode 100644 index 0000000000..972bb2edb0 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/engine/src/flutter/tools/relocation_packer/README.TXT b/engine/src/flutter/tools/relocation_packer/README.TXT new file mode 100644 index 0000000000..071ab5df2d --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/README.TXT @@ -0,0 +1,135 @@ +Introduction: +------------- + +Relative relocations are the bulk of dynamic relocations (the .rel.dyn +or .rela.dyn sections) in libchrome..so. The ELF standard +representation of them is wasteful. + +Packing uses a combination of run length encoding, delta encoding, and LEB128 +encoding to store them more efficiently. Packed relocations are placed in +a new .android.rel.dyn or .android.rela.dyn section. Packing reduces +the footprint of libchrome..so in the filesystem, in APK downloads, +and in memory when loaded on the device. + +A packed libchrome..so is designed so that it can be loaded directly +on Android, but requires the explicit support of a crazy linker that has been +extended to understand packed relocations. Packed relocations are currently +only supported on ARM. + +A packed libchrome..so cannot currently be used with the standard +Android runtime linker. + +See src/*.h for design and implementation notes. + + +Notes: +------ + +Packing does not adjust debug data. An unstripped libchrome..so +can be packed and will run, but may no longer be useful for debugging. + +Unpacking on the device requires the explicit support of an extended crazy +linker. Adds the following new .dynamic tags, used by the crazy linker to +find the packed .android.rel.dyn or .android.rela.dyn section data: + + DT_ANDROID_REL_OFFSET = DT_LOOS (Operating System specific: 0x6000000d) + - The offset of packed relocation data in libchrome..so + DT_ANDROID_REL_SIZE = DT_LOOS + 1 (Operating System Specific: 0x6000000e) + - The size of packed relocation data in bytes + +32 bit ARM libraries use relocations without addends. 64 bit ARM libraries +use relocations with addends. The packing strategy necessarily differs for +the two relocation types. + +Where libchrome..so contains relocations without addends, the format +of .android.rel.dyn data is: + + "APR1" identifier + N: the number of count-delta pairs in the encoding + A: the initial offset + N * C,D: N count-delta pairs + +Where libchrome..so contains relocations with addends, the format +of .android.rela.dyn data is: + + "APA1" identifier + N: the number of addr-addend delta pairs in the encoding + N * A,V: N addr-addend delta pairs + +All numbers in the encoding stream are stored as LEB128 values. For details +see http://en.wikipedia.org/wiki/LEB128. + +The streaming unpacking algorithm for 32 bit ARM is: + + skip over "APR1" + pairs, addr = next leb128 value, next leb128 value + emit R_ARM_RELATIVE relocation with r_offset = addr + while pairs: + count, delta = next leb128 value, next leb128 value + while count: + addr += delta + emit R_ARM_RELATIVE relocation with r_offset = addr + count-- + pairs-- + +The streaming unpacking algorithm for 64 bit ARM is: + + skip over "APA1" + pairs = next signed leb128 value + addr, addend = 0, 0 + while pairs: + addr += next signed leb128 value + addend += next signed leb128 value + emit R_AARCH64_RELATIVE relocation with r_offset = addr, r_addend = addend + pairs-- + + +Usage instructions: +------------------- + +To pack relocations, add an empty .android.rel.dyn or .android.rela.dyn and +then run the tool: + + echo -n 'NULL' >/tmp/small + if file libchrome..so | grep -q 'ELF 32'; then + arm-linux-androideabi-objcopy + --add-section .android.rel.dyn=/tmp/small + libchrome..so libchrome..so.packed + else + aarch64-linux-android-objcopy + --add-section .android.rela.dyn=/tmp/small + libchrome..so libchrome..so.packed + fi + rm /tmp/small + relocation_packer libchrome..so.packed + +To unpack and restore the shared library to its original state: + + cp libchrome..so.packed unpackable + relocation_packer -u unpackable + if file libchrome..so | grep -q 'ELF 32'; then + arm-linux-androideabi-objcopy \ + --remove-section=.android.rel.dyn unpackable libchrome..so + else + aarch64-linux-android-objcopy \ + --remove-section=.android.rela.dyn unpackable libchrome..so + endif + rm unpackable + + +Bugs & TODOs: +------------- + +Requires two free slots in the .dynamic section. Uses these to add data that +tells the crazy linker where to find the packed relocation data. Fails +if insufficient free slots exist (use gold --spare-dynamic-slots to increase +the allocation). + +Requires libelf 0.158 or later. Earlier libelf releases may be buggy in +ways that prevent the packer from working correctly. + + +Testing: +-------- + +Unittests run under gtest, on the host system. diff --git a/engine/src/flutter/tools/relocation_packer/config.gni b/engine/src/flutter/tools/relocation_packer/config.gni new file mode 100644 index 0000000000..4cdd9455b3 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/config.gni @@ -0,0 +1,21 @@ +# Copyright 2014 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. + +relocation_packing_supported = target_cpu == "arm" || target_cpu == "arm64" + +if (relocation_packing_supported) { + relocation_packer_target = "//tools/relocation_packer($host_toolchain)" + relocation_packer_dir = + get_label_info("$relocation_packer_target", "root_out_dir") + relocation_packer_exe = "${relocation_packer_dir}/relocation_packer" + + if (target_cpu == "arm") { + relocations_have_addends = 0 + } else if (target_cpu == "arm64") { + relocations_have_addends = 1 + } +} else { + relocations_have_addends = 0 + relocation_packer_exe = "" +} diff --git a/engine/src/flutter/tools/relocation_packer/relocation_packer.gyp b/engine/src/flutter/tools/relocation_packer/relocation_packer.gyp new file mode 100644 index 0000000000..1e9c1b95bc --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/relocation_packer.gyp @@ -0,0 +1,161 @@ +# Copyright 2014 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. + +{ + 'variables': { + 'target_define%': 'TARGET_UNSUPPORTED', + 'conditions': [ + [ 'target_arch == "arm"', { + 'target_define': 'TARGET_ARM', + }], + [ 'target_arch == "arm64"', { + 'target_define': 'TARGET_ARM64', + }], + ], + }, + 'targets': [ + { + # GN: //tools/relocation_packer:lib_relocation_packer + 'target_name': 'lib_relocation_packer', + 'toolsets': ['host'], + 'type': 'static_library', + 'defines': [ + '<(target_define)', + ], + 'dependencies': [ + '../../third_party/elfutils/elfutils.gyp:libelf', + ], + 'sources': [ + 'src/debug.cc', + 'src/delta_encoder.cc', + 'src/elf_file.cc', + 'src/leb128.cc', + 'src/packer.cc', + 'src/sleb128.cc', + 'src/run_length_encoder.cc', + ], + }, + { + # GN: //tools/relocation_packer:relocation_packer + 'target_name': 'relocation_packer', + 'toolsets': ['host'], + 'type': 'executable', + 'defines': [ + '<(target_define)', + ], + 'dependencies': [ + '../../third_party/elfutils/elfutils.gyp:libelf', + 'lib_relocation_packer', + ], + 'sources': [ + 'src/main.cc', + ], + }, + { + # GN: //tools/relocation_packer:relocation_packer_unittests + 'target_name': 'relocation_packer_unittests', + 'toolsets': ['host'], + 'type': 'executable', + 'defines': [ + '<(target_define)', + ], + 'cflags': [ + '-DINTERMEDIATE_DIR="<(INTERMEDIATE_DIR)"', + ], + 'dependencies': [ + '../../testing/gtest.gyp:gtest', + 'lib_relocation_packer', + ], + 'include_dirs': [ + '../..', + ], + 'sources': [ + 'src/debug_unittest.cc', + 'src/delta_encoder_unittest.cc', + 'src/elf_file_unittest.cc', + 'src/leb128_unittest.cc', + 'src/packer_unittest.cc', + 'src/sleb128_unittest.cc', + 'src/run_length_encoder_unittest.cc', + 'src/run_all_unittests.cc', + ], + 'copies': [ + { + 'destination': '<(INTERMEDIATE_DIR)', + 'files': [ + 'test_data/elf_file_unittest_relocs_arm32.so', + 'test_data/elf_file_unittest_relocs_arm32_packed.so', + 'test_data/elf_file_unittest_relocs_arm64.so', + 'test_data/elf_file_unittest_relocs_arm64_packed.so', + ], + }, + ], + }, + + # Targets to build test data. These participate only in building test + # data for use with elf_file_unittest.cc, and are not part of the main + # relocation packer build. Unit test data files are checked in to the + # source tree as 'golden' data, and are not generated 'on the fly' by + # the build. + # + # See test_data/generate_elf_file_unittest_relocs.sh for instructions. + { + # GN: //tools/relocation_packer:relocation_packer_test_data + 'target_name': 'relocation_packer_test_data', + 'toolsets': ['target'], + 'type': 'shared_library', + 'cflags': [ + '-O0', + '-g0', + ], + 'sources': [ + 'test_data/elf_file_unittest_relocs.cc', + ], + }, + { + # GN: //tools/relocation_packer:relocation_packer_unittests_test_data + 'target_name': 'relocation_packer_unittests_test_data', + 'toolsets': ['target'], + 'type': 'none', + 'actions': [ + { + 'variables': { + 'test_file': '<(SHARED_LIB_DIR)/librelocation_packer_test_data.so', + 'conditions': [ + [ 'target_arch == "arm"', { + 'added_section': '.android.rel.dyn', + 'unpacked_output': 'elf_file_unittest_relocs_arm32.so', + 'packed_output': 'elf_file_unittest_relocs_arm32_packed.so', + }], + [ 'target_arch == "arm64"', { + 'added_section': '.android.rela.dyn', + 'unpacked_output': 'elf_file_unittest_relocs_arm64.so', + 'packed_output': 'elf_file_unittest_relocs_arm64_packed.so', + }], + ], + }, + 'action_name': 'generate_relocation_packer_test_data', + 'inputs': [ + 'test_data/generate_elf_file_unittest_relocs.py', + '<(PRODUCT_DIR)/relocation_packer', + '<(test_file)', + ], + 'outputs': [ + '<(INTERMEDIATE_DIR)/<(unpacked_output)', + '<(INTERMEDIATE_DIR)/<(packed_output)', + ], + 'action': [ + 'python', 'test_data/generate_elf_file_unittest_relocs.py', + '--android-pack-relocations=<(PRODUCT_DIR)/relocation_packer', + '--android-objcopy=<(android_objcopy)', + '--added-section=<(added_section)', + '--test-file=<(test_file)', + '--unpacked-output=<(INTERMEDIATE_DIR)/<(unpacked_output)', + '--packed-output=<(INTERMEDIATE_DIR)/<(packed_output)', + ], + }, + ], + }, + ], +} diff --git a/engine/src/flutter/tools/relocation_packer/src/debug.cc b/engine/src/flutter/tools/relocation_packer/src/debug.cc new file mode 100644 index 0000000000..29d7ab0674 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/debug.cc @@ -0,0 +1,55 @@ +// Copyright 2014 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. + +#include "debug.h" + +#include +#include +#include + +namespace relocation_packer { + +// Construct a new message logger. Prints if level is less than or equal to +// the level set with SetVerbose() and predicate is true. +Logger::Logger(Severity severity, int level, bool predicate) { + severity_ = severity; + level_ = level; + predicate_ = predicate; +} + +// On destruction, flush and print the strings accumulated. Abort if FATAL. +Logger::~Logger() { + if (predicate_) { + if (level_ <= max_level_) { + std::ostream* log = severity_ == INFO ? info_stream_ : error_stream_; + std::string tag; + switch (severity_) { + case INFO: tag = "INFO"; break; + case WARNING: tag = "WARNING"; break; + case ERROR: tag = "ERROR"; break; + case FATAL: tag = "FATAL"; break; + } + stream_.flush(); + *log << tag << ": " << stream_.str() << std::endl; + } + if (severity_ == FATAL) + abort(); + } +} + +// Reset to initial state. +void Logger::Reset() { + max_level_ = -1; + info_stream_ = &std::cout; + error_stream_ = &std::cerr; +} + +// Verbosity. Not thread-safe. +int Logger::max_level_ = -1; + +// Logging streams. Not thread-safe. +std::ostream* Logger::info_stream_ = &std::cout; +std::ostream* Logger::error_stream_ = &std::cerr; + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/debug.h b/engine/src/flutter/tools/relocation_packer/src/debug.h new file mode 100644 index 0000000000..99db94862c --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/debug.h @@ -0,0 +1,115 @@ +// Copyright 2014 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. + +// Logging and checks. Avoids a dependency on base. +// +// LOG(tag) prints messages. Tags are INFO, WARNING, ERROR and FATAL. +// INFO prints to stdout, the others to stderr. FATAL aborts after printing. +// +// LOG_IF(tag, predicate) logs if predicate evaluates to true, else silent. +// +// VLOG(level) logs INFO messages where level is less than or equal to the +// verbosity level set with SetVerbose(). +// +// VLOG_IF(level, predicate) logs INFO if predicate evaluates to true, +// else silent. +// +// CHECK(predicate) logs a FATAL error if predicate is false. +// NOTREACHED() always aborts. +// Log streams can be changed with SetStreams(). Logging is not thread-safe. +// + +#ifndef TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ +#define TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ + +#include +#include +#include + +namespace relocation_packer { + +class Logger { + public: + enum Severity {INFO = 0, WARNING, ERROR, FATAL}; + + // Construct a new message logger. Prints if level is less than or + // equal to the level set with SetVerbose() and predicate is true. + // |severity| is an enumerated severity. + // |level| is the verbosity level. + // |predicate| controls if the logger prints or is silent. + Logger(Severity severity, int level, bool predicate); + + // On destruction, flush and print the strings accumulated in stream_. + ~Logger(); + + // Return the stream for this logger. + std::ostream& GetStream() { return stream_; } + + // Set verbosity level. Messages with a level less than or equal to + // this level are printed, others are discarded. Static, not thread-safe. + static void SetVerbose(int level) { max_level_ = level; } + + // Set info and error logging streams. Static, not thread-safe. + static void SetStreams(std::ostream* info_stream, + std::ostream* error_stream) { + info_stream_ = info_stream; + error_stream_ = error_stream; + } + + // Reset to initial state. + static void Reset(); + + private: + // Message severity, verbosity level, and predicate. + Severity severity_; + int level_; + bool predicate_; + + // String stream, accumulates message text. + std::ostringstream stream_; + + // Verbosity for INFO messages. Not thread-safe. + static int max_level_; + + // Logging streams. Not thread-safe. + static std::ostream* info_stream_; + static std::ostream* error_stream_; +}; + +} // namespace relocation_packer + +// Make logging severities visible globally. +typedef relocation_packer::Logger::Severity LogSeverity; +static const LogSeverity INFO = LogSeverity::INFO; +static const LogSeverity WARNING = LogSeverity::WARNING; +static const LogSeverity ERROR = LogSeverity::ERROR; +static const LogSeverity FATAL = LogSeverity::FATAL; + +// LOG(severity) prints a message with the given severity, and aborts if +// severity is FATAL. LOG_IF(severity, predicate) does the same but only if +// predicate is true. INT_MIN is guaranteed to be less than or equal to +// any verbosity level. +#define LOG(severity) \ + (relocation_packer::Logger(severity, INT_MIN, true).GetStream()) +#define LOG_IF(severity, predicate) \ + (relocation_packer::Logger(severity, INT_MIN, (predicate)).GetStream()) + +// VLOG(level) prints its message as INFO if level is less than or equal to +// the current verbosity level. +#define VLOG(level) \ + (relocation_packer::Logger(INFO, (level), true).GetStream()) +#define VLOG_IF(level, predicate) \ + (relocation_packer::Logger(INFO, (level), (predicate)).GetStream()) + +// CHECK(predicate) fails with a FATAL log message if predicate is false. +#define CHECK(predicate) (LOG_IF(FATAL, !(predicate)) \ + << __FILE__ << ":" << __LINE__ << ": " \ + << __FUNCTION__ << ": CHECK '" #predicate "' failed") + +// NOTREACHED() always fails with a FATAL log message. +#define NOTREACHED(_) (LOG(FATAL) \ + << __FILE__ << ":" << __LINE__ << ": " \ + << __FUNCTION__ << ": NOTREACHED() hit") + +#endif // TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ diff --git a/engine/src/flutter/tools/relocation_packer/src/debug_unittest.cc b/engine/src/flutter/tools/relocation_packer/src/debug_unittest.cc new file mode 100644 index 0000000000..1b65cd16e2 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/debug_unittest.cc @@ -0,0 +1,122 @@ +// Copyright 2014 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. + +#include "debug.h" + +#include +#include "testing/gtest/include/gtest/gtest.h" + +namespace relocation_packer { + +TEST(Debug, Log) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + LOG(INFO) << "INFO log message"; + LOG(WARNING) << "WARNING log message"; + LOG(ERROR) << "ERROR log message"; + + EXPECT_EQ("INFO: INFO log message\n", info.str()); + EXPECT_EQ("WARNING: WARNING log message\n" + "ERROR: ERROR log message\n", error.str()); + Logger::Reset(); +} + +TEST(Debug, LogIf) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + LOG_IF(INFO, true) << "INFO log message"; + LOG_IF(INFO, false) << "INFO log message, SHOULD NOT PRINT"; + LOG_IF(WARNING, true) << "WARNING log message"; + LOG_IF(WARNING, false) << "WARNING log message, SHOULD NOT PRINT"; + LOG_IF(ERROR, true) << "ERROR log message"; + LOG_IF(ERROR, false) << "ERROR log message, SHOULD NOT PRINT"; + LOG_IF(FATAL, false) << "FATAL log message, SHOULD NOT PRINT"; + + EXPECT_EQ("INFO: INFO log message\n", info.str()); + EXPECT_EQ("WARNING: WARNING log message\n" + "ERROR: ERROR log message\n", error.str()); + Logger::Reset(); +} + +TEST(Debug, Vlog) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + VLOG(0) << "VLOG 0 INFO log message, SHOULD NOT PRINT"; + VLOG(1) << "VLOG 1 INFO log message, SHOULD NOT PRINT"; + VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("", info.str()); + EXPECT_EQ("", error.str()); + + Logger::SetVerbose(1); + + VLOG(0) << "VLOG 0 INFO log message"; + VLOG(1) << "VLOG 1 INFO log message"; + VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("INFO: VLOG 0 INFO log message\n" + "INFO: VLOG 1 INFO log message\n", info.str()); + EXPECT_EQ("", error.str()); + Logger::Reset(); +} + +TEST(Debug, VlogIf) { + Logger::Reset(); + std::ostringstream info; + std::ostringstream error; + Logger::SetStreams(&info, &error); + + VLOG_IF(0, true) << "VLOG 0 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(1, true) << "VLOG 1 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("", info.str()); + EXPECT_EQ("", error.str()); + + Logger::SetVerbose(1); + + VLOG_IF(0, true) << "VLOG 0 INFO log message"; + VLOG_IF(0, false) << "VLOG 0 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(1, true) << "VLOG 1 INFO log message"; + VLOG_IF(1, false) << "VLOG 1 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + VLOG_IF(2, false) << "VLOG 2 INFO log message, SHOULD NOT PRINT"; + + EXPECT_EQ("INFO: VLOG 0 INFO log message\n" + "INFO: VLOG 1 INFO log message\n", info.str()); + EXPECT_EQ("", error.str()); + Logger::Reset(); +} + +TEST(DebugDeathTest, Fatal) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Logger::Reset(); + EXPECT_DEATH(LOG(FATAL) << "FATAL log message", "FATAL: FATAL log message"); + EXPECT_DEATH( + LOG_IF(FATAL, true) << "FATAL log message", "FATAL: FATAL log message"); +} + +TEST(DebugDeathTest, Check) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Logger::Reset(); + CHECK(0 == 0); + EXPECT_DEATH(CHECK(0 == 1), "FATAL: .*:.*: .*: CHECK '0 == 1' failed"); +} + +TEST(DebugDeathTest, NotReached) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + Logger::Reset(); + EXPECT_DEATH(NOTREACHED(), "FATAL: .*:.*: .*: NOTREACHED\\(\\) hit"); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/delta_encoder.cc b/engine/src/flutter/tools/relocation_packer/src/delta_encoder.cc new file mode 100644 index 0000000000..69cc91a511 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/delta_encoder.cc @@ -0,0 +1,72 @@ +// Copyright 2014 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. + +#include "delta_encoder.h" + +#include + +#include "debug.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode relative relocations with addends into a delta encoded (packed) +// representation. Represented as simple r_offset and r_addend delta pairs, +// with an implicit neutral element at the start. +void RelocationDeltaCodec::Encode(const std::vector& relocations, + std::vector* packed) { + // One relocation is sufficient for delta encoding. + if (relocations.size() < 1) + return; + + // Start with the element count, then append the delta pairs. + packed->push_back(relocations.size()); + + ELF::Addr offset = 0; + ELF::Sxword addend = 0; + + for (size_t i = 0; i < relocations.size(); ++i) { + const ELF::Rela* relocation = &relocations[i]; + CHECK(ELF_R_TYPE(relocation->r_info) == ELF::kRelativeRelocationCode); + + packed->push_back(relocation->r_offset - offset); + offset = relocation->r_offset; + packed->push_back(relocation->r_addend - addend); + addend = relocation->r_addend; + } +} + +// Decode relative relocations with addends from a delta encoded (packed) +// representation. +void RelocationDeltaCodec::Decode(const std::vector& packed, + std::vector* relocations) { + // We need at least one packed pair after the packed pair count to be + // able to unpack. + if (packed.size() < 3) + return; + + // Ensure that the packed data offers enough pairs. There may be zero + // padding on it that we ignore. + CHECK(static_cast(packed[0]) <= (packed.size() - 1) >> 1); + + ELF::Addr offset = 0; + ELF::Sxword addend = 0; + + // The first packed vector element is the pairs count. Start uncondensing + // pairs at the second, and finish at the end of the pairs data. + const size_t pairs_count = packed[0]; + for (size_t i = 1; i < 1 + (pairs_count << 1); i += 2) { + offset += packed[i]; + addend += packed[i + 1]; + + // Generate a relocation for this offset and addend pair. + ELF::Rela relocation; + relocation.r_offset = offset; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocation.r_addend = addend; + relocations->push_back(relocation); + } +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/delta_encoder.h b/engine/src/flutter/tools/relocation_packer/src/delta_encoder.h new file mode 100644 index 0000000000..498b6d1af6 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/delta_encoder.h @@ -0,0 +1,80 @@ +// Copyright 2014 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. + +// Delta encode and decode relative relocations with addends. +// +// Relative relocations are the bulk of dynamic relocations (the +// .rel.dyn or .rela.dyn sections) in libchrome..so, and the ELF +// standard representation of them is wasteful. .rel.dyn contains +// relocations without addends, .rela.dyn relocations with addends. +// +// A relocation with an addend is 12 bytes on 32 bit platforms and 24 bytes +// on 64 bit plaforms. It is split into offset, info, and addend fields. +// Offsets strictly increase, and each is commonly a few bytes different +// from its predecessor. Addends are less well behaved. The info field is +// constant. Example, from 'readelf -x4 libchrome..so' 64 bit: +// +// offset info +// 80949303 00000000 03040000 00000000 ................ +// addend offset +// fc015b00 00000000 88949303 00000000 ..[............. +// info addend +// 03040000 00000000 24025b00 00000000 ........$.[..... +// offset info +// 90949303 00000000 03040000 00000000 ................ +// addend offset +// 3c025b00 00000000 98949303 00000000 <.[............. +// info addend +// 03040000 00000000 50025b00 00000000 ........P.[..... +// +// The offset strictly increases, but the addend is unpredictable, so run +// length encoding will not work well with this data. We can however pack +// with delta encoding. The upper four bytes of the eight byte offset and +// addend are invariably zeroes. The difference between adjacent offsets +// is almost always small, and between adjacent addends is often small. And +// info is constant and can be eliminated. +// +// Delta encoding reduces the size of the data modestly, so that the first +// three relocations above can be represented as: +// +// initial offset initial addend offset delta addend delta +// 00000000 03939480 00000000 005b01fc 00000000 00000008 00000000 00000028 +// offset delta addend delta ... +// 00000000 00000008 00000000 0000009f +// +// The addend delta can be negative as well as positive, but overall the +// deltas have a much smaller range than the input data. When encoded as +// signed LEB128 the total data reduction becomes useful. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ +#define TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ + +#include + +#include "elf.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// A RelocationDeltaCodec packs vectors of relative relocations with +// addends into more compact forms, and unpacks them to reproduce the +// pre-packed data. +class RelocationDeltaCodec { + public: + // Encode relative relocations with addends into a more compact form. + // |relocations| is a vector of relative relocation with addend structs. + // |packed| is the vector of packed words into which relocations are packed. + static void Encode(const std::vector& relocations, + std::vector* packed); + + // Decode relative relocations with addends from their more compact form. + // |packed| is the vector of packed relocations. + // |relocations| is a vector of unpacked relative relocations. + static void Decode(const std::vector& packed, + std::vector* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_DELTA_ENCODER_H_ diff --git a/engine/src/flutter/tools/relocation_packer/src/delta_encoder_unittest.cc b/engine/src/flutter/tools/relocation_packer/src/delta_encoder_unittest.cc new file mode 100644 index 0000000000..b9bf39adbe --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/delta_encoder_unittest.cc @@ -0,0 +1,150 @@ +// Copyright 2014 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. + +#include "delta_encoder.h" + +#include +#include "elf.h" +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void AddRelocation(ELF::Addr addr, + ELF::Sxword addend, + std::vector* relocations) { + ELF::Rela relocation; + relocation.r_offset = addr; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocation.r_addend = addend; + relocations->push_back(relocation); +} + +bool CheckRelocation(ELF::Addr addr, + ELF::Sxword addend, + const ELF::Rela& relocation) { + return relocation.r_offset == addr && + ELF_R_SYM(relocation.r_info) == 0 && + ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode && + relocation.r_addend == addend; +} + +} // namespace + +namespace relocation_packer { + +TEST(Delta, Encode) { + std::vector relocations; + std::vector packed; + + RelocationDeltaCodec codec; + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(0, packed.size()); + + // Initial relocation. + AddRelocation(0xf00d0000, 10000, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(3, packed.size()); + // One pair present. + EXPECT_EQ(1, packed[0]); + // Delta from the neutral element is the initial relocation. + EXPECT_EQ(0xf00d0000, packed[1]); + EXPECT_EQ(10000, packed[2]); + + // Add a second relocation, 4 byte offset delta, 12 byte addend delta. + AddRelocation(0xf00d0004, 10012, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(5, packed.size()); + // Two pairs present. + EXPECT_EQ(2, packed[0]); + // Delta from the neutral element is the initial relocation. + EXPECT_EQ(0xf00d0000, packed[1]); + EXPECT_EQ(10000, packed[2]); + // 4 byte offset delta, 12 byte addend delta. + EXPECT_EQ(4, packed[3]); + EXPECT_EQ(12, packed[4]); + + // Add a third relocation, 4 byte offset delta, 12 byte addend delta. + AddRelocation(0xf00d0008, 10024, &relocations); + + // Add three more relocations, 8 byte offset deltas, -24 byte addend deltas. + AddRelocation(0xf00d0010, 10000, &relocations); + AddRelocation(0xf00d0018, 9976, &relocations); + AddRelocation(0xf00d0020, 9952, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(13, packed.size()); + // Six pairs present. + EXPECT_EQ(6, packed[0]); + // Initial relocation. + EXPECT_EQ(0xf00d0000, packed[1]); + EXPECT_EQ(10000, packed[2]); + // Two relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_EQ(4, packed[3]); + EXPECT_EQ(12, packed[4]); + EXPECT_EQ(4, packed[5]); + EXPECT_EQ(12, packed[6]); + // Three relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_EQ(8, packed[7]); + EXPECT_EQ(-24, packed[8]); + EXPECT_EQ(8, packed[9]); + EXPECT_EQ(-24, packed[10]); + EXPECT_EQ(8, packed[11]); + EXPECT_EQ(-24, packed[12]); +} + +TEST(Delta, Decode) { + std::vector packed; + std::vector relocations; + + RelocationDeltaCodec codec; + codec.Decode(packed, &relocations); + + EXPECT_EQ(0, relocations.size()); + + // Six pairs. + packed.push_back(6); + // Initial relocation. + packed.push_back(0xc0de0000); + packed.push_back(10000); + // Two relocations, 4 byte offset deltas, 12 byte addend deltas. + packed.push_back(4); + packed.push_back(12); + packed.push_back(4); + packed.push_back(12); + // Three relocations, 8 byte offset deltas, -24 byte addend deltas. + packed.push_back(8); + packed.push_back(-24); + packed.push_back(8); + packed.push_back(-24); + packed.push_back(8); + packed.push_back(-24); + + relocations.clear(); + codec.Decode(packed, &relocations); + + EXPECT_EQ(6, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xc0de0000, 10000, relocations[0])); + // Two relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xc0de0004, 10012, relocations[1])); + EXPECT_TRUE(CheckRelocation(0xc0de0008, 10024, relocations[2])); + // Three relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xc0de0010, 10000, relocations[3])); + EXPECT_TRUE(CheckRelocation(0xc0de0018, 9976, relocations[4])); + EXPECT_TRUE(CheckRelocation(0xc0de0020, 9952, relocations[5])); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/elf_file.cc b/engine/src/flutter/tools/relocation_packer/src/elf_file.cc new file mode 100644 index 0000000000..5853c9d7d5 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/elf_file.cc @@ -0,0 +1,1317 @@ +// Copyright 2014 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. + +// Implementation notes: +// +// We need to remove a piece from the ELF shared library. However, we also +// want to ensure that code and data loads at the same addresses as before +// packing, so that tools like breakpad can still match up addresses found +// in any crash dumps with data extracted from the pre-packed version of +// the shared library. +// +// Arranging this means that we have to split one of the LOAD segments into +// two. Unfortunately, the program headers are located at the very start +// of the shared library file, so expanding the program header section +// would cause a lot of consequent changes to files offsets that we don't +// really want to have to handle. +// +// Luckily, though, there is a segment that is always present and always +// unused on Android; the GNU_STACK segment. What we do is to steal that +// and repurpose it to be one of the split LOAD segments. We then have to +// sort LOAD segments by offset to keep the crazy linker happy. +// +// All of this takes place in SplitProgramHeadersForHole(), used on packing, +// and is unraveled on unpacking in CoalesceProgramHeadersForHole(). See +// commentary on those functions for an example of this segment stealing +// in action. + +#include "elf_file.h" + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "elf_traits.h" +#include "libelf.h" +#include "packer.h" + +namespace relocation_packer { + +// Stub identifier written to 'null out' packed data, "NULL". +static const uint32_t kStubIdentifier = 0x4c4c554eu; + +// Out-of-band dynamic tags used to indicate the offset and size of the +// android packed relocations section. +static const ELF::Sword DT_ANDROID_REL_OFFSET = DT_LOOS; +static const ELF::Sword DT_ANDROID_REL_SIZE = DT_LOOS + 1; + +// Alignment to preserve, in bytes. This must be at least as large as the +// largest d_align and sh_addralign values found in the loaded file. +// Out of caution for RELRO page alignment, we preserve to a complete target +// page. See http://www.airs.com/blog/archives/189. +static const size_t kPreserveAlignment = 4096; + +// Alignment values used by ld and gold for the GNU_STACK segment. Different +// linkers write different values; the actual value is immaterial on Android +// because it ignores GNU_STACK segments. However, it is useful for binary +// comparison and unit test purposes if packing and unpacking can preserve +// them through a round-trip. +static const size_t kLdGnuStackSegmentAlignment = 16; +static const size_t kGoldGnuStackSegmentAlignment = 0; + +namespace { + +// Get section data. Checks that the section has exactly one data entry, +// so that the section size and the data size are the same. True in +// practice for all sections we resize when packing or unpacking. Done +// by ensuring that a call to elf_getdata(section, data) returns NULL as +// the next data entry. +Elf_Data* GetSectionData(Elf_Scn* section) { + Elf_Data* data = elf_getdata(section, NULL); + CHECK(data && elf_getdata(section, data) == NULL); + return data; +} + +// Rewrite section data. Allocates new data and makes it the data element's +// buffer. Relies on program exit to free allocated data. +void RewriteSectionData(Elf_Scn* section, + const void* section_data, + size_t size) { + Elf_Data* data = GetSectionData(section); + CHECK(size == data->d_size); + uint8_t* area = new uint8_t[size]; + memcpy(area, section_data, size); + data->d_buf = area; +} + +// Verbose ELF header logging. +void VerboseLogElfHeader(const ELF::Ehdr* elf_header) { + VLOG(1) << "e_phoff = " << elf_header->e_phoff; + VLOG(1) << "e_shoff = " << elf_header->e_shoff; + VLOG(1) << "e_ehsize = " << elf_header->e_ehsize; + VLOG(1) << "e_phentsize = " << elf_header->e_phentsize; + VLOG(1) << "e_phnum = " << elf_header->e_phnum; + VLOG(1) << "e_shnum = " << elf_header->e_shnum; + VLOG(1) << "e_shstrndx = " << elf_header->e_shstrndx; +} + +// Verbose ELF program header logging. +void VerboseLogProgramHeader(size_t program_header_index, + const ELF::Phdr* program_header) { + std::string type; + switch (program_header->p_type) { + case PT_NULL: type = "NULL"; break; + case PT_LOAD: type = "LOAD"; break; + case PT_DYNAMIC: type = "DYNAMIC"; break; + case PT_INTERP: type = "INTERP"; break; + case PT_PHDR: type = "PHDR"; break; + case PT_GNU_RELRO: type = "GNU_RELRO"; break; + case PT_GNU_STACK: type = "GNU_STACK"; break; + case PT_ARM_EXIDX: type = "EXIDX"; break; + default: type = "(OTHER)"; break; + } + VLOG(1) << "phdr[" << program_header_index << "] : " << type; + VLOG(1) << " p_offset = " << program_header->p_offset; + VLOG(1) << " p_vaddr = " << program_header->p_vaddr; + VLOG(1) << " p_paddr = " << program_header->p_paddr; + VLOG(1) << " p_filesz = " << program_header->p_filesz; + VLOG(1) << " p_memsz = " << program_header->p_memsz; + VLOG(1) << " p_flags = " << program_header->p_flags; + VLOG(1) << " p_align = " << program_header->p_align; +} + +// Verbose ELF section header logging. +void VerboseLogSectionHeader(const std::string& section_name, + const ELF::Shdr* section_header) { + VLOG(1) << "section " << section_name; + VLOG(1) << " sh_addr = " << section_header->sh_addr; + VLOG(1) << " sh_offset = " << section_header->sh_offset; + VLOG(1) << " sh_size = " << section_header->sh_size; + VLOG(1) << " sh_addralign = " << section_header->sh_addralign; +} + +// Verbose ELF section data logging. +void VerboseLogSectionData(const Elf_Data* data) { + VLOG(1) << " data"; + VLOG(1) << " d_buf = " << data->d_buf; + VLOG(1) << " d_off = " << data->d_off; + VLOG(1) << " d_size = " << data->d_size; + VLOG(1) << " d_align = " << data->d_align; +} + +} // namespace + +// Load the complete ELF file into a memory image in libelf, and identify +// the .rel.dyn or .rela.dyn, .dynamic, and .android.rel.dyn or +// .android.rela.dyn sections. No-op if the ELF file has already been loaded. +bool ElfFile::Load() { + if (elf_) + return true; + + Elf* elf = elf_begin(fd_, ELF_C_RDWR, NULL); + CHECK(elf); + + if (elf_kind(elf) != ELF_K_ELF) { + LOG(ERROR) << "File not in ELF format"; + return false; + } + + ELF::Ehdr* elf_header = ELF::getehdr(elf); + if (!elf_header) { + LOG(ERROR) << "Failed to load ELF header: " << elf_errmsg(elf_errno()); + return false; + } + if (elf_header->e_machine != ELF::kMachine) { + LOG(ERROR) << "ELF file architecture is not " << ELF::Machine(); + return false; + } + if (elf_header->e_type != ET_DYN) { + LOG(ERROR) << "ELF file is not a shared object"; + return false; + } + + // Require that our endianness matches that of the target, and that both + // are little-endian. Safe for all current build/target combinations. + const int endian = elf_header->e_ident[EI_DATA]; + CHECK(endian == ELFDATA2LSB); + CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__); + + // Also require that the file class is as expected. + const int file_class = elf_header->e_ident[EI_CLASS]; + CHECK(file_class == ELF::kFileClass); + + VLOG(1) << "endian = " << endian << ", file class = " << file_class; + VerboseLogElfHeader(elf_header); + + const ELF::Phdr* elf_program_header = ELF::getphdr(elf); + CHECK(elf_program_header); + + const ELF::Phdr* dynamic_program_header = NULL; + for (size_t i = 0; i < elf_header->e_phnum; ++i) { + const ELF::Phdr* program_header = &elf_program_header[i]; + VerboseLogProgramHeader(i, program_header); + + if (program_header->p_type == PT_DYNAMIC) { + CHECK(dynamic_program_header == NULL); + dynamic_program_header = program_header; + } + } + CHECK(dynamic_program_header != NULL); + + size_t string_index; + elf_getshdrstrndx(elf, &string_index); + + // Notes of the dynamic relocations, packed relocations, and .dynamic + // sections. Found while iterating sections, and later stored in class + // attributes. + Elf_Scn* found_relocations_section = NULL; + Elf_Scn* found_android_relocations_section = NULL; + Elf_Scn* found_dynamic_section = NULL; + + // Notes of relocation section types seen. We require one or the other of + // these; both is unsupported. + bool has_rel_relocations = false; + bool has_rela_relocations = false; + + Elf_Scn* section = NULL; + while ((section = elf_nextscn(elf, section)) != NULL) { + const ELF::Shdr* section_header = ELF::getshdr(section); + std::string name = elf_strptr(elf, string_index, section_header->sh_name); + VerboseLogSectionHeader(name, section_header); + + // Note relocation section types. + if (section_header->sh_type == SHT_REL) { + has_rel_relocations = true; + } + if (section_header->sh_type == SHT_RELA) { + has_rela_relocations = true; + } + + // Note special sections as we encounter them. + if ((name == ".rel.dyn" || name == ".rela.dyn") && + section_header->sh_size > 0) { + found_relocations_section = section; + } + if ((name == ".android.rel.dyn" || name == ".android.rela.dyn") && + section_header->sh_size > 0) { + found_android_relocations_section = section; + } + if (section_header->sh_offset == dynamic_program_header->p_offset) { + found_dynamic_section = section; + } + + // Ensure we preserve alignment, repeated later for the data block(s). + CHECK(section_header->sh_addralign <= kPreserveAlignment); + + Elf_Data* data = NULL; + while ((data = elf_getdata(section, data)) != NULL) { + CHECK(data->d_align <= kPreserveAlignment); + VerboseLogSectionData(data); + } + } + + // Loading failed if we did not find the required special sections. + if (!found_relocations_section) { + LOG(ERROR) << "Missing or empty .rel.dyn or .rela.dyn section"; + return false; + } + if (!found_android_relocations_section) { + LOG(ERROR) << "Missing or empty .android.rel.dyn or .android.rela.dyn " + << "section (to fix, run with --help and follow the " + << "pre-packing instructions)"; + return false; + } + if (!found_dynamic_section) { + LOG(ERROR) << "Missing .dynamic section"; + return false; + } + + // Loading failed if we could not identify the relocations type. + if (!has_rel_relocations && !has_rela_relocations) { + LOG(ERROR) << "No relocations sections found"; + return false; + } + if (has_rel_relocations && has_rela_relocations) { + LOG(ERROR) << "Multiple relocations sections with different types found, " + << "not currently supported"; + return false; + } + + elf_ = elf; + relocations_section_ = found_relocations_section; + dynamic_section_ = found_dynamic_section; + android_relocations_section_ = found_android_relocations_section; + relocations_type_ = has_rel_relocations ? REL : RELA; + return true; +} + +namespace { + +// Helper for ResizeSection(). Adjust the main ELF header for the hole. +void AdjustElfHeaderForHole(ELF::Ehdr* elf_header, + ELF::Off hole_start, + ssize_t hole_size) { + if (elf_header->e_phoff > hole_start) { + elf_header->e_phoff += hole_size; + VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff; + } + if (elf_header->e_shoff > hole_start) { + elf_header->e_shoff += hole_size; + VLOG(1) << "e_shoff adjusted to " << elf_header->e_shoff; + } +} + +// Helper for ResizeSection(). Adjust all section headers for the hole. +void AdjustSectionHeadersForHole(Elf* elf, + ELF::Off hole_start, + ssize_t hole_size) { + size_t string_index; + elf_getshdrstrndx(elf, &string_index); + + Elf_Scn* section = NULL; + while ((section = elf_nextscn(elf, section)) != NULL) { + ELF::Shdr* section_header = ELF::getshdr(section); + std::string name = elf_strptr(elf, string_index, section_header->sh_name); + + if (section_header->sh_offset > hole_start) { + section_header->sh_offset += hole_size; + VLOG(1) << "section " << name + << " sh_offset adjusted to " << section_header->sh_offset; + } + } +} + +// Helper for ResizeSection(). Adjust the offsets of any program headers +// that have offsets currently beyond the hole start. +void AdjustProgramHeaderOffsets(ELF::Phdr* program_headers, + size_t count, + ELF::Phdr* ignored_1, + ELF::Phdr* ignored_2, + ELF::Off hole_start, + ssize_t hole_size) { + for (size_t i = 0; i < count; ++i) { + ELF::Phdr* program_header = &program_headers[i]; + + if (program_header == ignored_1 || program_header == ignored_2) + continue; + + if (program_header->p_offset > hole_start) { + // The hole start is past this segment, so adjust offset. + program_header->p_offset += hole_size; + VLOG(1) << "phdr[" << i + << "] p_offset adjusted to "<< program_header->p_offset; + } + } +} + +// Helper for ResizeSection(). Find the first loadable segment in the +// file. We expect it to map from file offset zero. +ELF::Phdr* FindFirstLoadSegment(ELF::Phdr* program_headers, + size_t count) { + ELF::Phdr* first_loadable_segment = NULL; + + for (size_t i = 0; i < count; ++i) { + ELF::Phdr* program_header = &program_headers[i]; + + if (program_header->p_type == PT_LOAD && + program_header->p_offset == 0 && + program_header->p_vaddr == 0 && + program_header->p_paddr == 0) { + first_loadable_segment = program_header; + } + } + LOG_IF(FATAL, !first_loadable_segment) + << "Cannot locate a LOAD segment with address and offset zero"; + + return first_loadable_segment; +} + +// Helper for ResizeSection(). Deduce the alignment that the PT_GNU_STACK +// segment will use. Determined by sensing the linker that was used to +// create the shared library. +size_t DeduceGnuStackSegmentAlignment(Elf* elf) { + size_t string_index; + elf_getshdrstrndx(elf, &string_index); + + Elf_Scn* section = NULL; + size_t gnu_stack_segment_alignment = kLdGnuStackSegmentAlignment; + + while ((section = elf_nextscn(elf, section)) != NULL) { + const ELF::Shdr* section_header = ELF::getshdr(section); + std::string name = elf_strptr(elf, string_index, section_header->sh_name); + + if (name == ".note.gnu.gold-version") { + gnu_stack_segment_alignment = kGoldGnuStackSegmentAlignment; + break; + } + } + + return gnu_stack_segment_alignment; +} + +// Helper for ResizeSection(). Find the PT_GNU_STACK segment, and check +// that it contains what we expect so we can restore it on unpack if needed. +ELF::Phdr* FindUnusedGnuStackSegment(Elf* elf, + ELF::Phdr* program_headers, + size_t count) { + ELF::Phdr* unused_segment = NULL; + const size_t stack_alignment = DeduceGnuStackSegmentAlignment(elf); + + for (size_t i = 0; i < count; ++i) { + ELF::Phdr* program_header = &program_headers[i]; + + if (program_header->p_type == PT_GNU_STACK && + program_header->p_offset == 0 && + program_header->p_vaddr == 0 && + program_header->p_paddr == 0 && + program_header->p_filesz == 0 && + program_header->p_memsz == 0 && + program_header->p_flags == (PF_R | PF_W) && + program_header->p_align == stack_alignment) { + unused_segment = program_header; + } + } + LOG_IF(FATAL, !unused_segment) + << "Cannot locate the expected GNU_STACK segment"; + + return unused_segment; +} + +// Helper for ResizeSection(). Find the segment that was the first loadable +// one before we split it into two. This is the one into which we coalesce +// the split segments on unpacking. +ELF::Phdr* FindOriginalFirstLoadSegment(ELF::Phdr* program_headers, + size_t count) { + const ELF::Phdr* first_loadable_segment = + FindFirstLoadSegment(program_headers, count); + + ELF::Phdr* original_first_loadable_segment = NULL; + + for (size_t i = 0; i < count; ++i) { + ELF::Phdr* program_header = &program_headers[i]; + + // The original first loadable segment is the one that follows on from + // the one we wrote on split to be the current first loadable segment. + if (program_header->p_type == PT_LOAD && + program_header->p_offset == first_loadable_segment->p_filesz) { + original_first_loadable_segment = program_header; + } + } + LOG_IF(FATAL, !original_first_loadable_segment) + << "Cannot locate the LOAD segment that follows a LOAD at offset zero"; + + return original_first_loadable_segment; +} + +// Helper for ResizeSection(). Find the segment that contains the hole. +Elf_Scn* FindSectionContainingHole(Elf* elf, + ELF::Off hole_start, + ssize_t hole_size) { + Elf_Scn* section = NULL; + Elf_Scn* last_unholed_section = NULL; + + while ((section = elf_nextscn(elf, section)) != NULL) { + const ELF::Shdr* section_header = ELF::getshdr(section); + + // Because we get here after section headers have been adjusted for the + // hole, we need to 'undo' that adjustment to give a view of the original + // sections layout. + ELF::Off offset = section_header->sh_offset; + if (section_header->sh_offset >= hole_start) { + offset -= hole_size; + } + + if (offset <= hole_start) { + last_unholed_section = section; + } + } + LOG_IF(FATAL, !last_unholed_section) + << "Cannot identify the section before the one containing the hole"; + + // The section containing the hole is the one after the last one found + // by the loop above. + Elf_Scn* holed_section = elf_nextscn(elf, last_unholed_section); + LOG_IF(FATAL, !holed_section) + << "Cannot identify the section containing the hole"; + + return holed_section; +} + +// Helper for ResizeSection(). Find the last section contained in a segment. +Elf_Scn* FindLastSectionInSegment(Elf* elf, + ELF::Phdr* program_header, + ELF::Off hole_start, + ssize_t hole_size) { + const ELF::Off segment_end = + program_header->p_offset + program_header->p_filesz; + + Elf_Scn* section = NULL; + Elf_Scn* last_section = NULL; + + while ((section = elf_nextscn(elf, section)) != NULL) { + const ELF::Shdr* section_header = ELF::getshdr(section); + + // As above, 'undo' any section offset adjustment to give a view of the + // original sections layout. + ELF::Off offset = section_header->sh_offset; + if (section_header->sh_offset >= hole_start) { + offset -= hole_size; + } + + if (offset < segment_end) { + last_section = section; + } + } + LOG_IF(FATAL, !last_section) + << "Cannot identify the last section in the given segment"; + + return last_section; +} + +// Helper for ResizeSection(). Order loadable segments by their offsets. +// The crazy linker contains assumptions about loadable segment ordering, +// and it is better if we do not break them. +void SortOrderSensitiveProgramHeaders(ELF::Phdr* program_headers, + size_t count) { + std::vector orderable; + + // Collect together orderable program headers. These are all the LOAD + // segments, and any GNU_STACK that may be present (removed on packing, + // but replaced on unpacking). + for (size_t i = 0; i < count; ++i) { + ELF::Phdr* program_header = &program_headers[i]; + + if (program_header->p_type == PT_LOAD || + program_header->p_type == PT_GNU_STACK) { + orderable.push_back(program_header); + } + } + + // Order these program headers so that any PT_GNU_STACK is last, and + // the LOAD segments that precede it appear in offset order. Uses + // insertion sort. + for (size_t i = 1; i < orderable.size(); ++i) { + for (size_t j = i; j > 0; --j) { + ELF::Phdr* first = orderable[j - 1]; + ELF::Phdr* second = orderable[j]; + + if (!(first->p_type == PT_GNU_STACK || + first->p_offset > second->p_offset)) { + break; + } + std::swap(*first, *second); + } + } +} + +// Helper for ResizeSection(). The GNU_STACK program header is unused in +// Android, so we can repurpose it here. Before packing, the program header +// table contains something like: +// +// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +// LOAD 0x000000 0x00000000 0x00000000 0x1efc818 0x1efc818 R E 0x1000 +// LOAD 0x1efd008 0x01efe008 0x01efe008 0x17ec3c 0x1a0324 RW 0x1000 +// DYNAMIC 0x205ec50 0x0205fc50 0x0205fc50 0x00108 0x00108 RW 0x4 +// GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0 +// +// The hole in the file is in the first of these. In order to preserve all +// load addresses, what we do is to turn the GNU_STACK into a new LOAD entry +// that maps segments up to where we created the hole, adjust the first LOAD +// entry so that it maps segments after that, adjust any other program +// headers whose offset is after the hole start, and finally order the LOAD +// segments by offset, to give: +// +// Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +// LOAD 0x000000 0x00000000 0x00000000 0x14ea4 0x14ea4 R E 0x1000 +// LOAD 0x014ea4 0x00212ea4 0x00212ea4 0x1cea164 0x1cea164 R E 0x1000 +// DYNAMIC 0x1e60c50 0x0205fc50 0x0205fc50 0x00108 0x00108 RW 0x4 +// LOAD 0x1cff008 0x01efe008 0x01efe008 0x17ec3c 0x1a0324 RW 0x1000 +// +// We work out the split points by finding the .rel.dyn or .rela.dyn section +// that contains the hole, and by finding the last section in a given segment. +// +// To unpack, we reverse the above to leave the file as it was originally. +void SplitProgramHeadersForHole(Elf* elf, + ELF::Off hole_start, + ssize_t hole_size) { + CHECK(hole_size < 0); + const ELF::Ehdr* elf_header = ELF::getehdr(elf); + CHECK(elf_header); + + ELF::Phdr* elf_program_header = ELF::getphdr(elf); + CHECK(elf_program_header); + + const size_t program_header_count = elf_header->e_phnum; + + // Locate the segment that we can overwrite to form the new LOAD entry, + // and the segment that we are going to split into two parts. + ELF::Phdr* spliced_header = + FindUnusedGnuStackSegment(elf, elf_program_header, program_header_count); + ELF::Phdr* split_header = + FindFirstLoadSegment(elf_program_header, program_header_count); + + VLOG(1) << "phdr[" << split_header - elf_program_header << "] split"; + VLOG(1) << "phdr[" << spliced_header - elf_program_header << "] new LOAD"; + + // Find the section that contains the hole. We split on the section that + // follows it. + Elf_Scn* holed_section = + FindSectionContainingHole(elf, hole_start, hole_size); + + size_t string_index; + elf_getshdrstrndx(elf, &string_index); + + ELF::Shdr* section_header = ELF::getshdr(holed_section); + std::string name = elf_strptr(elf, string_index, section_header->sh_name); + VLOG(1) << "section " << name << " split after"; + + // Find the last section in the segment we are splitting. + Elf_Scn* last_section = + FindLastSectionInSegment(elf, split_header, hole_start, hole_size); + + section_header = ELF::getshdr(last_section); + name = elf_strptr(elf, string_index, section_header->sh_name); + VLOG(1) << "section " << name << " split end"; + + // Split on the section following the holed one, and up to (but not + // including) the section following the last one in the split segment. + Elf_Scn* split_section = elf_nextscn(elf, holed_section); + LOG_IF(FATAL, !split_section) + << "No section follows the section that contains the hole"; + Elf_Scn* end_section = elf_nextscn(elf, last_section); + LOG_IF(FATAL, !end_section) + << "No section follows the last section in the segment being split"; + + // Split the first portion of split_header into spliced_header. + const ELF::Shdr* split_section_header = ELF::getshdr(split_section); + spliced_header->p_type = split_header->p_type; + spliced_header->p_offset = split_header->p_offset; + spliced_header->p_vaddr = split_header->p_vaddr; + spliced_header->p_paddr = split_header->p_paddr; + CHECK(split_header->p_filesz == split_header->p_memsz); + spliced_header->p_filesz = split_section_header->sh_offset; + spliced_header->p_memsz = split_section_header->sh_offset; + spliced_header->p_flags = split_header->p_flags; + spliced_header->p_align = split_header->p_align; + + // Now rewrite split_header to remove the part we spliced from it. + const ELF::Shdr* end_section_header = ELF::getshdr(end_section); + split_header->p_offset = spliced_header->p_filesz; + CHECK(split_header->p_vaddr == split_header->p_paddr); + split_header->p_vaddr = split_section_header->sh_addr; + split_header->p_paddr = split_section_header->sh_addr; + CHECK(split_header->p_filesz == split_header->p_memsz); + split_header->p_filesz = + end_section_header->sh_offset - spliced_header->p_filesz; + split_header->p_memsz = + end_section_header->sh_offset - spliced_header->p_filesz; + + // Adjust the offsets of all program headers that are not one of the pair + // we just created by splitting. + AdjustProgramHeaderOffsets(elf_program_header, + program_header_count, + spliced_header, + split_header, + hole_start, + hole_size); + + // Finally, order loadable segments by offset/address. The crazy linker + // contains assumptions about loadable segment ordering. + SortOrderSensitiveProgramHeaders(elf_program_header, + program_header_count); +} + +// Helper for ResizeSection(). Undo the work of SplitProgramHeadersForHole(). +void CoalesceProgramHeadersForHole(Elf* elf, + ELF::Off hole_start, + ssize_t hole_size) { + CHECK(hole_size > 0); + const ELF::Ehdr* elf_header = ELF::getehdr(elf); + CHECK(elf_header); + + ELF::Phdr* elf_program_header = ELF::getphdr(elf); + CHECK(elf_program_header); + + const size_t program_header_count = elf_header->e_phnum; + + // Locate the segment that we overwrote to form the new LOAD entry, and + // the segment that we split into two parts on packing. + ELF::Phdr* spliced_header = + FindFirstLoadSegment(elf_program_header, program_header_count); + ELF::Phdr* split_header = + FindOriginalFirstLoadSegment(elf_program_header, program_header_count); + + VLOG(1) << "phdr[" << spliced_header - elf_program_header << "] stack"; + VLOG(1) << "phdr[" << split_header - elf_program_header << "] coalesce"; + + // Find the last section in the second segment we are coalescing. + Elf_Scn* last_section = + FindLastSectionInSegment(elf, split_header, hole_start, hole_size); + + size_t string_index; + elf_getshdrstrndx(elf, &string_index); + + const ELF::Shdr* section_header = ELF::getshdr(last_section); + std::string name = elf_strptr(elf, string_index, section_header->sh_name); + VLOG(1) << "section " << name << " coalesced"; + + // Rewrite the coalesced segment into split_header. + const ELF::Shdr* last_section_header = ELF::getshdr(last_section); + split_header->p_offset = spliced_header->p_offset; + CHECK(split_header->p_vaddr == split_header->p_paddr); + split_header->p_vaddr = spliced_header->p_vaddr; + split_header->p_paddr = spliced_header->p_vaddr; + CHECK(split_header->p_filesz == split_header->p_memsz); + split_header->p_filesz = + last_section_header->sh_offset + last_section_header->sh_size; + split_header->p_memsz = + last_section_header->sh_offset + last_section_header->sh_size; + + // Reconstruct the original GNU_STACK segment into spliced_header. + const size_t stack_alignment = DeduceGnuStackSegmentAlignment(elf); + spliced_header->p_type = PT_GNU_STACK; + spliced_header->p_offset = 0; + spliced_header->p_vaddr = 0; + spliced_header->p_paddr = 0; + spliced_header->p_filesz = 0; + spliced_header->p_memsz = 0; + spliced_header->p_flags = PF_R | PF_W; + spliced_header->p_align = stack_alignment; + + // Adjust the offsets of all program headers that are not one of the pair + // we just coalesced. + AdjustProgramHeaderOffsets(elf_program_header, + program_header_count, + spliced_header, + split_header, + hole_start, + hole_size); + + // Finally, order loadable segments by offset/address. The crazy linker + // contains assumptions about loadable segment ordering. + SortOrderSensitiveProgramHeaders(elf_program_header, + program_header_count); +} + +// Helper for ResizeSection(). Rewrite program headers. +void RewriteProgramHeadersForHole(Elf* elf, + ELF::Off hole_start, + ssize_t hole_size) { + // If hole_size is negative then we are removing a piece of the file, and + // we want to split program headers so that we keep the same addresses + // for text and data. If positive, then we are putting that piece of the + // file back in, so we coalesce the previously split program headers. + if (hole_size < 0) + SplitProgramHeadersForHole(elf, hole_start, hole_size); + else if (hole_size > 0) + CoalesceProgramHeadersForHole(elf, hole_start, hole_size); +} + +// Helper for ResizeSection(). Locate and return the dynamic section. +Elf_Scn* GetDynamicSection(Elf* elf) { + const ELF::Ehdr* elf_header = ELF::getehdr(elf); + CHECK(elf_header); + + const ELF::Phdr* elf_program_header = ELF::getphdr(elf); + CHECK(elf_program_header); + + // Find the program header that describes the dynamic section. + const ELF::Phdr* dynamic_program_header = NULL; + for (size_t i = 0; i < elf_header->e_phnum; ++i) { + const ELF::Phdr* program_header = &elf_program_header[i]; + + if (program_header->p_type == PT_DYNAMIC) { + dynamic_program_header = program_header; + } + } + CHECK(dynamic_program_header); + + // Now find the section with the same offset as this program header. + Elf_Scn* dynamic_section = NULL; + Elf_Scn* section = NULL; + while ((section = elf_nextscn(elf, section)) != NULL) { + ELF::Shdr* section_header = ELF::getshdr(section); + + if (section_header->sh_offset == dynamic_program_header->p_offset) { + dynamic_section = section; + } + } + CHECK(dynamic_section != NULL); + + return dynamic_section; +} + +// Helper for ResizeSection(). Adjust the .dynamic section for the hole. +template +void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, + ELF::Off hole_start, + ssize_t hole_size) { + Elf_Data* data = GetSectionData(dynamic_section); + + const ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector dynamics( + dynamic_base, + dynamic_base + data->d_size / sizeof(dynamics[0])); + + for (size_t i = 0; i < dynamics.size(); ++i) { + ELF::Dyn* dynamic = &dynamics[i]; + const ELF::Sword tag = dynamic->d_tag; + + // DT_RELSZ or DT_RELASZ indicate the overall size of relocations. + // Only one will be present. Adjust by hole size. + if (tag == DT_RELSZ || tag == DT_RELASZ) { + dynamic->d_un.d_val += hole_size; + VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag + << " d_val adjusted to " << dynamic->d_un.d_val; + } + + // DT_RELCOUNT or DT_RELACOUNT hold the count of relative relocations. + // Only one will be present. Packing reduces it to the alignment + // padding, if any; unpacking restores it to its former value. The + // crazy linker does not use it, but we update it anyway. + if (tag == DT_RELCOUNT || tag == DT_RELACOUNT) { + // Cast sizeof to a signed type to avoid the division result being + // promoted into an unsigned size_t. + const ssize_t sizeof_rel = static_cast(sizeof(Rel)); + dynamic->d_un.d_val += hole_size / sizeof_rel; + VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag + << " d_val adjusted to " << dynamic->d_un.d_val; + } + + // DT_RELENT and DT_RELAENT do not change, but make sure they are what + // we expect. Only one will be present. + if (tag == DT_RELENT || tag == DT_RELAENT) { + CHECK(dynamic->d_un.d_val == sizeof(Rel)); + } + } + + void* section_data = &dynamics[0]; + size_t bytes = dynamics.size() * sizeof(dynamics[0]); + RewriteSectionData(dynamic_section, section_data, bytes); +} + +// Resize a section. If the new size is larger than the current size, open +// up a hole by increasing file offsets that come after the hole. If smaller +// than the current size, remove the hole by decreasing those offsets. +template +void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) { + ELF::Shdr* section_header = ELF::getshdr(section); + if (section_header->sh_size == new_size) + return; + + // Note if we are resizing the real dyn relocations. + size_t string_index; + elf_getshdrstrndx(elf, &string_index); + const std::string section_name = + elf_strptr(elf, string_index, section_header->sh_name); + const bool is_relocations_resize = + (section_name == ".rel.dyn" || section_name == ".rela.dyn"); + + // Require that the section size and the data size are the same. True + // in practice for all sections we resize when packing or unpacking. + Elf_Data* data = GetSectionData(section); + CHECK(data->d_off == 0 && data->d_size == section_header->sh_size); + + // Require that the section is not zero-length (that is, has allocated + // data that we can validly expand). + CHECK(data->d_size && data->d_buf); + + const ELF::Off hole_start = section_header->sh_offset; + const ssize_t hole_size = new_size - data->d_size; + + VLOG_IF(1, (hole_size > 0)) << "expand section size = " << data->d_size; + VLOG_IF(1, (hole_size < 0)) << "shrink section size = " << data->d_size; + + // Resize the data and the section header. + data->d_size += hole_size; + section_header->sh_size += hole_size; + + // Add the hole size to all offsets in the ELF file that are after the + // start of the hole. If the hole size is positive we are expanding the + // section to create a new hole; if negative, we are closing up a hole. + + // Start with the main ELF header. + ELF::Ehdr* elf_header = ELF::getehdr(elf); + AdjustElfHeaderForHole(elf_header, hole_start, hole_size); + + // Adjust all section headers. + AdjustSectionHeadersForHole(elf, hole_start, hole_size); + + // If resizing the dynamic relocations, rewrite the program headers to + // either split or coalesce segments, and adjust dynamic entries to match. + if (is_relocations_resize) { + RewriteProgramHeadersForHole(elf, hole_start, hole_size); + + Elf_Scn* dynamic_section = GetDynamicSection(elf); + AdjustDynamicSectionForHole(dynamic_section, hole_start, hole_size); + } +} + +// Find the first slot in a dynamics array with the given tag. The array +// always ends with a free (unused) element, and which we exclude from the +// search. Returns dynamics->size() if not found. +size_t FindDynamicEntry(ELF::Sword tag, + std::vector* dynamics) { + // Loop until the penultimate entry. We exclude the end sentinel. + for (size_t i = 0; i < dynamics->size() - 1; ++i) { + if (dynamics->at(i).d_tag == tag) + return i; + } + + // The tag was not found. + return dynamics->size(); +} + +// Replace the first free (unused) slot in a dynamics vector with the given +// value. The vector always ends with a free (unused) element, so the slot +// found cannot be the last one in the vector. +void AddDynamicEntry(const ELF::Dyn& dyn, + std::vector* dynamics) { + const size_t slot = FindDynamicEntry(DT_NULL, dynamics); + if (slot == dynamics->size()) { + LOG(FATAL) << "No spare dynamic array slots found " + << "(to fix, increase gold's --spare-dynamic-tags value)"; + } + + // Replace this entry with the one supplied. + dynamics->at(slot) = dyn; + VLOG(1) << "dynamic[" << slot << "] overwritten with " << dyn.d_tag; +} + +// Remove the element in the dynamics vector that matches the given tag with +// unused slot data. Shuffle the following elements up, and ensure that the +// last is the null sentinel. +void RemoveDynamicEntry(ELF::Sword tag, + std::vector* dynamics) { + const size_t slot = FindDynamicEntry(tag, dynamics); + CHECK(slot != dynamics->size()); + + // Remove this entry by shuffling up everything that follows. + for (size_t i = slot; i < dynamics->size() - 1; ++i) { + dynamics->at(i) = dynamics->at(i + 1); + VLOG(1) << "dynamic[" << i + << "] overwritten with dynamic[" << i + 1 << "]"; + } + + // Ensure that the end sentinel is still present. + CHECK(dynamics->at(dynamics->size() - 1).d_tag == DT_NULL); +} + +// Construct a null relocation without addend. +void NullRelocation(ELF::Rel* relocation) { + relocation->r_offset = 0; + relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode); +} + +// Construct a null relocation with addend. +void NullRelocation(ELF::Rela* relocation) { + relocation->r_offset = 0; + relocation->r_info = ELF_R_INFO(0, ELF::kNoRelocationCode); + relocation->r_addend = 0; +} + +// Pad relocations with the given number of null entries. Generates its +// null entry with the appropriate NullRelocation() invocation. +template +void PadRelocations(size_t count, std::vector* relocations) { + Rel null_relocation; + NullRelocation(&null_relocation); + std::vector padding(count, null_relocation); + relocations->insert(relocations->end(), padding.begin(), padding.end()); +} + +} // namespace + +// Remove relative entries from dynamic relocations and write as packed +// data into android packed relocations. +bool ElfFile::PackRelocations() { + // Load the ELF file into libelf. + if (!Load()) { + LOG(ERROR) << "Failed to load as ELF"; + return false; + } + + // Retrieve the current dynamic relocations section data. + Elf_Data* data = GetSectionData(relocations_section_); + + if (relocations_type_ == REL) { + // Convert data to a vector of relocations. + const ELF::Rel* relocations_base = reinterpret_cast(data->d_buf); + std::vector relocations( + relocations_base, + relocations_base + data->d_size / sizeof(relocations[0])); + + LOG(INFO) << "Relocations : REL"; + return PackTypedRelocations(relocations); + } + + if (relocations_type_ == RELA) { + // Convert data to a vector of relocations with addends. + const ELF::Rela* relocations_base = + reinterpret_cast(data->d_buf); + std::vector relocations( + relocations_base, + relocations_base + data->d_size / sizeof(relocations[0])); + + LOG(INFO) << "Relocations : RELA"; + return PackTypedRelocations(relocations); + } + + NOTREACHED(); + return false; +} + +// Helper for PackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. +template +bool ElfFile::PackTypedRelocations(const std::vector& relocations) { + // Filter relocations into those that are relative and others. + std::vector relative_relocations; + std::vector other_relocations; + + for (size_t i = 0; i < relocations.size(); ++i) { + const Rel& relocation = relocations[i]; + if (ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode) { + CHECK(ELF_R_SYM(relocation.r_info) == 0); + relative_relocations.push_back(relocation); + } else { + other_relocations.push_back(relocation); + } + } + LOG(INFO) << "Relative : " << relative_relocations.size() << " entries"; + LOG(INFO) << "Other : " << other_relocations.size() << " entries"; + LOG(INFO) << "Total : " << relocations.size() << " entries"; + + // If no relative relocations then we have nothing packable. Perhaps + // the shared object has already been packed? + if (relative_relocations.empty()) { + LOG(ERROR) << "No relative relocations found (already packed?)"; + return false; + } + + // If not padding fully, apply only enough padding to preserve alignment. + // Otherwise, pad so that we do not shrink the relocations section at all. + if (!is_padding_relocations_) { + // Calculate the size of the hole we will close up when we rewrite + // dynamic relocations. + ssize_t hole_size = + relative_relocations.size() * sizeof(relative_relocations[0]); + const ssize_t unaligned_hole_size = hole_size; + + // Adjust the actual hole size to preserve alignment. We always adjust + // by a whole number of NONE-type relocations. + while (hole_size % kPreserveAlignment) + hole_size -= sizeof(relative_relocations[0]); + LOG(INFO) << "Compaction : " << hole_size << " bytes"; + + // Adjusting for alignment may have removed any packing benefit. + if (hole_size == 0) { + LOG(INFO) << "Too few relative relocations to pack after alignment"; + return false; + } + + // Find the padding needed in other_relocations to preserve alignment. + // Ensure that we never completely empty the real relocations section. + size_t padding_bytes = unaligned_hole_size - hole_size; + if (padding_bytes == 0 && other_relocations.size() == 0) { + do { + padding_bytes += sizeof(relative_relocations[0]); + } while (padding_bytes % kPreserveAlignment); + } + CHECK(padding_bytes % sizeof(other_relocations[0]) == 0); + const size_t padding = padding_bytes / sizeof(other_relocations[0]); + + // Padding may have removed any packing benefit. + if (padding >= relative_relocations.size()) { + LOG(INFO) << "Too few relative relocations to pack after padding"; + return false; + } + + // Add null relocations to other_relocations to preserve alignment. + PadRelocations(padding, &other_relocations); + LOG(INFO) << "Alignment pad : " << padding << " relocations"; + } else { + // If padding, add NONE-type relocations to other_relocations to make it + // the same size as the the original relocations we read in. This makes + // the ResizeSection() below a no-op. + const size_t padding = relocations.size() - other_relocations.size(); + PadRelocations(padding, &other_relocations); + } + + // Pack relative relocations. + const size_t initial_bytes = + relative_relocations.size() * sizeof(relative_relocations[0]); + LOG(INFO) << "Unpacked relative: " << initial_bytes << " bytes"; + std::vector packed; + RelocationPacker packer; + packer.PackRelativeRelocations(relative_relocations, &packed); + const void* packed_data = &packed[0]; + const size_t packed_bytes = packed.size() * sizeof(packed[0]); + LOG(INFO) << "Packed relative: " << packed_bytes << " bytes"; + + // If we have insufficient relative relocations to form a run then + // packing fails. + if (packed.empty()) { + LOG(INFO) << "Too few relative relocations to pack"; + return false; + } + + // Run a loopback self-test as a check that packing is lossless. + std::vector unpacked; + packer.UnpackRelativeRelocations(packed, &unpacked); + CHECK(unpacked.size() == relative_relocations.size()); + CHECK(!memcmp(&unpacked[0], + &relative_relocations[0], + unpacked.size() * sizeof(unpacked[0]))); + + // Make sure packing saved some space. + if (packed_bytes >= initial_bytes) { + LOG(INFO) << "Packing relative relocations saves no space"; + return false; + } + + // Rewrite the current dynamic relocations section to be only the ARM + // non-relative relocations, then shrink it to size. + const void* section_data = &other_relocations[0]; + const size_t bytes = other_relocations.size() * sizeof(other_relocations[0]); + ResizeSection(elf_, relocations_section_, bytes); + RewriteSectionData(relocations_section_, section_data, bytes); + + // Rewrite the current packed android relocations section to hold the packed + // relative relocations. + ResizeSection(elf_, android_relocations_section_, packed_bytes); + RewriteSectionData(android_relocations_section_, packed_data, packed_bytes); + + // Rewrite .dynamic to include two new tags describing the packed android + // relocations. + Elf_Data* data = GetSectionData(dynamic_section_); + const ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector dynamics( + dynamic_base, + dynamic_base + data->d_size / sizeof(dynamics[0])); + // Use two of the spare slots to describe the packed section. + ELF::Shdr* section_header = ELF::getshdr(android_relocations_section_); + { + ELF::Dyn dyn; + dyn.d_tag = DT_ANDROID_REL_OFFSET; + dyn.d_un.d_ptr = section_header->sh_offset; + AddDynamicEntry(dyn, &dynamics); + } + { + ELF::Dyn dyn; + dyn.d_tag = DT_ANDROID_REL_SIZE; + dyn.d_un.d_val = section_header->sh_size; + AddDynamicEntry(dyn, &dynamics); + } + const void* dynamics_data = &dynamics[0]; + const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]); + RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes); + + Flush(); + return true; +} + +// Find packed relative relocations in the packed android relocations +// section, unpack them, and rewrite the dynamic relocations section to +// contain unpacked data. +bool ElfFile::UnpackRelocations() { + // Load the ELF file into libelf. + if (!Load()) { + LOG(ERROR) << "Failed to load as ELF"; + return false; + } + + // Retrieve the current packed android relocations section data. + Elf_Data* data = GetSectionData(android_relocations_section_); + + // Convert data to a vector of bytes. + const uint8_t* packed_base = reinterpret_cast(data->d_buf); + std::vector packed( + packed_base, + packed_base + data->d_size / sizeof(packed[0])); + + if (packed.size() > 3 && + packed[0] == 'A' && + packed[1] == 'P' && + packed[2] == 'R' && + packed[3] == '1') { + // Signature is APR1, unpack relocations. + CHECK(relocations_type_ == REL); + LOG(INFO) << "Relocations : REL"; + return UnpackTypedRelocations(packed); + } + + if (packed.size() > 3 && + packed[0] == 'A' && + packed[1] == 'P' && + packed[2] == 'A' && + packed[3] == '1') { + // Signature is APA1, unpack relocations with addends. + CHECK(relocations_type_ == RELA); + LOG(INFO) << "Relocations : RELA"; + return UnpackTypedRelocations(packed); + } + + LOG(ERROR) << "Packed relative relocations not found (not packed?)"; + return false; +} + +// Helper for UnpackRelocations(). Rel type is one of ELF::Rel or ELF::Rela. +template +bool ElfFile::UnpackTypedRelocations(const std::vector& packed) { + // Unpack the data to re-materialize the relative relocations. + const size_t packed_bytes = packed.size() * sizeof(packed[0]); + LOG(INFO) << "Packed relative: " << packed_bytes << " bytes"; + std::vector relative_relocations; + RelocationPacker packer; + packer.UnpackRelativeRelocations(packed, &relative_relocations); + const size_t unpacked_bytes = + relative_relocations.size() * sizeof(relative_relocations[0]); + LOG(INFO) << "Unpacked relative: " << unpacked_bytes << " bytes"; + + // Retrieve the current dynamic relocations section data. + Elf_Data* data = GetSectionData(relocations_section_); + + // Interpret data as relocations. + const Rel* relocations_base = reinterpret_cast(data->d_buf); + std::vector relocations( + relocations_base, + relocations_base + data->d_size / sizeof(relocations[0])); + + std::vector other_relocations; + size_t padding = 0; + + // Filter relocations to locate any that are NONE-type. These will occur + // if padding was turned on for packing. + for (size_t i = 0; i < relocations.size(); ++i) { + const Rel& relocation = relocations[i]; + if (ELF_R_TYPE(relocation.r_info) != ELF::kNoRelocationCode) { + other_relocations.push_back(relocation); + } else { + ++padding; + } + } + LOG(INFO) << "Relative : " << relative_relocations.size() << " entries"; + LOG(INFO) << "Other : " << other_relocations.size() << " entries"; + + // If we found the same number of null relocation entries in the dynamic + // relocations section as we hold as unpacked relative relocations, then + // this is a padded file. + const bool is_padded = padding == relative_relocations.size(); + + // Unless padded, report by how much we expand the file. + if (!is_padded) { + // Calculate the size of the hole we will open up when we rewrite + // dynamic relocations. + ssize_t hole_size = + relative_relocations.size() * sizeof(relative_relocations[0]); + + // Adjust the hole size for the padding added to preserve alignment. + hole_size -= padding * sizeof(other_relocations[0]); + LOG(INFO) << "Expansion : " << hole_size << " bytes"; + } + + // Rewrite the current dynamic relocations section to be the relative + // relocations followed by other relocations. This is the usual order in + // which we find them after linking, so this action will normally put the + // entire dynamic relocations section back to its pre-split-and-packed state. + relocations.assign(relative_relocations.begin(), relative_relocations.end()); + relocations.insert(relocations.end(), + other_relocations.begin(), other_relocations.end()); + const void* section_data = &relocations[0]; + const size_t bytes = relocations.size() * sizeof(relocations[0]); + LOG(INFO) << "Total : " << relocations.size() << " entries"; + ResizeSection(elf_, relocations_section_, bytes); + RewriteSectionData(relocations_section_, section_data, bytes); + + // Nearly empty the current packed android relocations section. Leaves a + // four-byte stub so that some data remains allocated to the section. + // This is a convenience which allows us to re-pack this file again without + // having to remove the section and then add a new small one with objcopy. + // The way we resize sections relies on there being some data in a section. + ResizeSection( + elf_, android_relocations_section_, sizeof(kStubIdentifier)); + RewriteSectionData( + android_relocations_section_, &kStubIdentifier, sizeof(kStubIdentifier)); + + // Rewrite .dynamic to remove two tags describing packed android relocations. + data = GetSectionData(dynamic_section_); + const ELF::Dyn* dynamic_base = reinterpret_cast(data->d_buf); + std::vector dynamics( + dynamic_base, + dynamic_base + data->d_size / sizeof(dynamics[0])); + RemoveDynamicEntry(DT_ANDROID_REL_OFFSET, &dynamics); + RemoveDynamicEntry(DT_ANDROID_REL_SIZE, &dynamics); + const void* dynamics_data = &dynamics[0]; + const size_t dynamics_bytes = dynamics.size() * sizeof(dynamics[0]); + RewriteSectionData(dynamic_section_, dynamics_data, dynamics_bytes); + + Flush(); + return true; +} + +// Flush rewritten shared object file data. +void ElfFile::Flush() { + // Flag all ELF data held in memory as needing to be written back to the + // file, and tell libelf that we have controlled the file layout. + elf_flagelf(elf_, ELF_C_SET, ELF_F_DIRTY); + elf_flagelf(elf_, ELF_C_SET, ELF_F_LAYOUT); + + // Write ELF data back to disk. + const off_t file_bytes = elf_update(elf_, ELF_C_WRITE); + CHECK(file_bytes > 0); + VLOG(1) << "elf_update returned: " << file_bytes; + + // Clean up libelf, and truncate the output file to the number of bytes + // written by elf_update(). + elf_end(elf_); + elf_ = NULL; + const int truncate = ftruncate(fd_, file_bytes); + CHECK(truncate == 0); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/elf_file.h b/engine/src/flutter/tools/relocation_packer/src/elf_file.h new file mode 100644 index 0000000000..6550274976 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/elf_file.h @@ -0,0 +1,134 @@ +// Copyright 2014 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. + +// ELF shared object file updates handler. +// +// Provides functions to remove relative relocations from the .rel.dyn +// or .rela.dyn sections and pack into .android.rel.dyn or .android.rela.dyn, +// and unpack to return the file to its pre-packed state. +// +// Files to be packed or unpacked must include an existing .android.rel.dyn +// or android.rela.dyn section. A standard libchrome..so will not +// contain this section, so the following can be used to add one: +// +// echo -n 'NULL' >/tmp/small +// if file libchrome..so | grep -q 'ELF 32'; then +// arm-linux-androideabi-objcopy +// --add-section .android.rel.dyn=/tmp/small +// libchrome..so libchrome..so.packed +// else +// aarch64-linux-android-objcopy +// --add-section .android.rela.dyn=/tmp/small +// libchrome..so libchrome..so.packed +// fi +// rm /tmp/small +// +// To use, open the file and pass the file descriptor to the constructor, +// then pack or unpack as desired. Packing or unpacking will flush the file +// descriptor on success. Example: +// +// int fd = open(..., O_RDWR); +// ElfFile elf_file(fd); +// bool status; +// if (is_packing) +// status = elf_file.PackRelocations(); +// else +// status = elf_file.UnpackRelocations(); +// close(fd); +// +// SetPadding() causes PackRelocations() to pad .rel.dyn or .rela.dyn with +// NONE-type entries rather than cutting a hole out of the shared object +// file. This keeps all load addresses and offsets constant, and enables +// easier debugging and testing. +// +// A packed shared object file has all of its relative relocations +// removed from .rel.dyn or .rela.dyn, and replaced as packed data in +// .android.rel.dyn or .android.rela.dyn respectively. The resulting file +// is shorter than its non-packed original. +// +// Unpacking a packed file restores the file to its non-packed state, by +// expanding the packed data in .android.rel.dyn or .android.rela.dyn, +// combining the relative relocations with the data already in .rel.dyn +// or .rela.dyn, and then writing back the now expanded section. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ +#define TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ + +#include +#include + +#include "elf.h" +#include "libelf.h" +#include "packer.h" + +namespace relocation_packer { + +// An ElfFile reads shared objects, and shuttles relative relocations +// between .rel.dyn or .rela.dyn and .android.rel.dyn or .android.rela.dyn +// sections. +class ElfFile { + public: + explicit ElfFile(int fd) + : fd_(fd), is_padding_relocations_(false), elf_(NULL), + relocations_section_(NULL), dynamic_section_(NULL), + android_relocations_section_(NULL), relocations_type_(NONE) {} + ~ElfFile() {} + + // Set padding mode. When padding, PackRelocations() will not shrink + // the .rel.dyn or .rela.dyn section, but instead replace relative with + // NONE-type entries. + // |flag| is true to pad .rel.dyn or .rela.dyn, false to shrink it. + inline void SetPadding(bool flag) { is_padding_relocations_ = flag; } + + // Transfer relative relocations from .rel.dyn or .rela.dyn to a packed + // representation in .android.rel.dyn or .android.rela.dyn. Returns true + // on success. + bool PackRelocations(); + + // Transfer relative relocations from a packed representation in + // .android.rel.dyn or .android.rela.dyn to .rel.dyn or .rela.dyn. Returns + // true on success. + bool UnpackRelocations(); + + private: + // Load a new ElfFile from a filedescriptor. If flushing, the file must + // be open for read/write. Returns true on successful ELF file load. + // |fd| is an open file descriptor for the shared object. + bool Load(); + + // Templated packer, helper for PackRelocations(). Rel type is one of + // ELF::Rel or ELF::Rela. + template + bool PackTypedRelocations(const std::vector& relocations); + + // Templated unpacker, helper for UnpackRelocations(). Rel type is one of + // ELF::Rel or ELF::Rela. + template + bool UnpackTypedRelocations(const std::vector& packed); + + // Write ELF file changes. + void Flush(); + + // File descriptor opened on the shared object. + int fd_; + + // If set, pad rather than shrink .rel.dyn or .rela.dyn. Primarily for + // debugging, allows packing to be checked without affecting load addresses. + bool is_padding_relocations_; + + // Libelf handle, assigned by Load(). + Elf* elf_; + + // Sections that we manipulate, assigned by Load(). + Elf_Scn* relocations_section_; + Elf_Scn* dynamic_section_; + Elf_Scn* android_relocations_section_; + + // Relocation type found, assigned by Load(). + enum { NONE = 0, REL, RELA } relocations_type_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_FILE_H_ diff --git a/engine/src/flutter/tools/relocation_packer/src/elf_file_unittest.cc b/engine/src/flutter/tools/relocation_packer/src/elf_file_unittest.cc new file mode 100644 index 0000000000..37abd0d95d --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/elf_file_unittest.cc @@ -0,0 +1,164 @@ +// Copyright 2014 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. + +#include "elf_file.h" + +#include +#include +#include +#include +#include +#include "debug.h" +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Macro stringification. +// https://gcc.gnu.org/onlinedocs/cpp/Stringification.html +#define XSTR(S) STR(S) +#define STR(S) #S + +namespace { + +void GetDataFilePath(const char* name, std::string* path) { + std::string data_dir; + + const char* bindir = getenv("bindir"); + if (bindir) { + data_dir = std::string(bindir); + } else { + // Test data is in the gyp INTERMEDIATE_DIR subdirectory of the directory + // that contains the current binary. + char path[PATH_MAX]; + memset(path, 0, sizeof(path)); + ASSERT_NE(-1, readlink("/proc/self/exe", path, sizeof(path) - 1)); + + data_dir = std::string(path); + size_t pos = data_dir.rfind('/'); + ASSERT_NE(std::string::npos, pos); + + data_dir.erase(pos + 1); + data_dir += std::string(XSTR(INTERMEDIATE_DIR)); + } + + *path = data_dir + "/" + name; +} + +void OpenRelocsTestFile(const char* name, FILE** stream) { + std::string path; + GetDataFilePath(name, &path); + + FILE* testfile = fopen(path.c_str(), "rb"); + ASSERT_FALSE(testfile == NULL); + + FILE* temporary = tmpfile(); + ASSERT_FALSE(temporary == NULL); + + static const size_t buffer_size = 4096; + unsigned char buffer[buffer_size]; + + size_t bytes; + do { + bytes = fread(buffer, 1, sizeof(buffer), testfile); + ASSERT_EQ(bytes, fwrite(buffer, 1, bytes, temporary)); + } while (bytes > 0); + + ASSERT_EQ(0, fclose(testfile)); + ASSERT_EQ(0, fseek(temporary, 0, SEEK_SET)); + ASSERT_EQ(0, lseek(fileno(temporary), 0, SEEK_SET)); + + *stream = temporary; +} + +void OpenRelocsTestFiles(FILE** relocs_so, FILE** packed_relocs_so) { + const char* arch = NULL; + if (ELF::kMachine == EM_ARM) { + arch = "arm32"; + } else if (ELF::kMachine == EM_AARCH64) { + arch = "arm64"; + } + ASSERT_FALSE(arch == NULL); + + const std::string base = std::string("elf_file_unittest_relocs_") + arch; + const std::string relocs = base + ".so"; + const std::string packed_relocs = base + "_packed.so"; + + OpenRelocsTestFile(relocs.c_str(), relocs_so); + OpenRelocsTestFile(packed_relocs.c_str(), packed_relocs_so); +} + +void CloseRelocsTestFile(FILE* temporary) { + fclose(temporary); +} + +void CloseRelocsTestFiles(FILE* relocs_so, FILE* packed_relocs_so) { + CloseRelocsTestFile(relocs_so); + CloseRelocsTestFile(packed_relocs_so); +} + +void CheckFileContentsEqual(FILE* first, FILE* second) { + ASSERT_EQ(0, fseek(first, 0, SEEK_SET)); + ASSERT_EQ(0, fseek(second, 0, SEEK_SET)); + + static const size_t buffer_size = 4096; + unsigned char first_buffer[buffer_size]; + unsigned char second_buffer[buffer_size]; + + do { + size_t first_read = fread(first_buffer, 1, sizeof(first_buffer), first); + size_t second_read = fread(second_buffer, 1, sizeof(second_buffer), second); + + EXPECT_EQ(first_read, second_read); + EXPECT_EQ(0, memcmp(first_buffer, second_buffer, first_read)); + } while (!feof(first) && !feof(second)); + + EXPECT_TRUE(feof(first) && feof(second)); +} + +} // namespace + +namespace relocation_packer { + +TEST(ElfFile, PackRelocations) { + ASSERT_NE(EV_NONE, elf_version(EV_CURRENT)); + + FILE* relocs_so = NULL; + FILE* packed_relocs_so = NULL; + OpenRelocsTestFiles(&relocs_so, &packed_relocs_so); + if (HasFatalFailure()) + return; + + ElfFile elf_file(fileno(relocs_so)); + + // Ensure unpacking fails (not packed). + EXPECT_FALSE(elf_file.UnpackRelocations()); + + // Pack relocations, and check files are now identical. + EXPECT_TRUE(elf_file.PackRelocations()); + CheckFileContentsEqual(relocs_so, packed_relocs_so); + + CloseRelocsTestFiles(relocs_so, packed_relocs_so); +} + +TEST(ElfFile, UnpackRelocations) { + ASSERT_NE(EV_NONE, elf_version(EV_CURRENT)); + + FILE* relocs_so = NULL; + FILE* packed_relocs_so = NULL; + OpenRelocsTestFiles(&relocs_so, &packed_relocs_so); + if (HasFatalFailure()) + return; + + ElfFile elf_file(fileno(packed_relocs_so)); + + // Ensure packing fails (already packed). + EXPECT_FALSE(elf_file.PackRelocations()); + + // Unpack golden relocations, and check files are now identical. + EXPECT_TRUE(elf_file.UnpackRelocations()); + CheckFileContentsEqual(packed_relocs_so, relocs_so); + + CloseRelocsTestFiles(relocs_so, packed_relocs_so); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/elf_traits.h b/engine/src/flutter/tools/relocation_packer/src/elf_traits.h new file mode 100644 index 0000000000..1004767542 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/elf_traits.h @@ -0,0 +1,103 @@ +// Copyright 2014 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. + +// Target-specific ELF type traits. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ +#define TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ + +#include "elf.h" +#include "libelf.h" + +// The TARGET_ macro controls which Elf types we expect and handle. +// Either TARGET_ARM or TARGET_ARM64 must be defined, but not both. + +#if !defined(TARGET_ARM) && !defined(TARGET_ARM64) +# error "Unsupported target, define one of TARGET_ARM or TARGET_ARM64" +#elif defined(TARGET_ARM) && defined(TARGET_ARM64) +# error "Define one of TARGET_ARM or TARGET_ARM64, but not both" +#endif + +// TODO(simonb): Eliminate these once AARCH64 appears reliably in elf.h. +#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#endif +#ifndef R_AARCH64_RELATIVE +#define R_AARCH64_RELATIVE 1027 +#endif +#ifndef R_AARCH64_NONE +#define R_AARCH64_NONE 0 +#endif + +// ELF is a traits structure used to provide convenient aliases for +// 32/64 bit Elf types and functions, depending on the target specified. + +#if defined(TARGET_ARM) +struct ELF { + typedef Elf32_Addr Addr; + typedef Elf32_Dyn Dyn; + typedef Elf32_Ehdr Ehdr; + typedef Elf32_Off Off; + typedef Elf32_Phdr Phdr; + typedef Elf32_Rel Rel; + typedef Elf32_Rela Rela; + typedef Elf32_Shdr Shdr; + typedef Elf32_Sword Sword; + typedef Elf32_Sxword Sxword; + typedef Elf32_Sym Sym; + typedef Elf32_Word Word; + typedef Elf32_Xword Xword; + + static inline Ehdr* getehdr(Elf* elf) { return elf32_getehdr(elf); } + static inline Phdr* getphdr(Elf* elf) { return elf32_getphdr(elf); } + static inline Shdr* getshdr(Elf_Scn* scn) { return elf32_getshdr(scn); } + + enum { kMachine = EM_ARM }; + enum { kFileClass = ELFCLASS32 }; + enum { kRelativeRelocationCode = R_ARM_RELATIVE }; + enum { kNoRelocationCode = R_ARM_NONE }; + + static inline const char* Machine() { return "ARM"; } + +# define ELF_R_SYM(val) ELF32_R_SYM(val) +# define ELF_R_TYPE(val) ELF32_R_TYPE(val) +# define ELF_R_INFO(sym, type) ELF32_R_INFO(sym, type) +# define ELF_ST_TYPE(val) ELF32_ST_TYPE(val) +}; + +#elif defined(TARGET_ARM64) +struct ELF { + typedef Elf64_Addr Addr; + typedef Elf64_Dyn Dyn; + typedef Elf64_Ehdr Ehdr; + typedef Elf64_Off Off; + typedef Elf64_Phdr Phdr; + typedef Elf64_Rel Rel; + typedef Elf64_Rela Rela; + typedef Elf64_Shdr Shdr; + typedef Elf64_Sword Sword; + typedef Elf64_Sxword Sxword; + typedef Elf64_Sym Sym; + typedef Elf64_Word Word; + typedef Elf64_Xword Xword; + + static inline Ehdr* getehdr(Elf* elf) { return elf64_getehdr(elf); } + static inline Phdr* getphdr(Elf* elf) { return elf64_getphdr(elf); } + static inline Shdr* getshdr(Elf_Scn* scn) { return elf64_getshdr(scn); } + + enum { kMachine = EM_AARCH64 }; + enum { kFileClass = ELFCLASS64 }; + enum { kRelativeRelocationCode = R_AARCH64_RELATIVE }; + enum { kNoRelocationCode = R_AARCH64_NONE }; + + static inline const char* Machine() { return "ARM64"; } + +# define ELF_R_SYM(val) ELF64_R_SYM(val) +# define ELF_R_TYPE(val) ELF64_R_TYPE(val) +# define ELF_R_INFO(sym, type) ELF64_R_INFO(sym, type) +# define ELF_ST_TYPE(val) ELF64_ST_TYPE(val) +}; +#endif + +#endif // TOOLS_RELOCATION_PACKER_SRC_ELF_TRAITS_H_ diff --git a/engine/src/flutter/tools/relocation_packer/src/leb128.cc b/engine/src/flutter/tools/relocation_packer/src/leb128.cc new file mode 100644 index 0000000000..b48739c324 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/leb128.cc @@ -0,0 +1,69 @@ +// Copyright 2014 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. + +#include "leb128.h" + +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Empty constructor and destructor to silence chromium-style. +Leb128Encoder::Leb128Encoder() { } +Leb128Encoder::~Leb128Encoder() { } + +// Add a single value to the encoding. Values are encoded with variable +// length. The least significant 7 bits of each byte hold 7 bits of data, +// and the most significant bit is set on each byte except the last. +void Leb128Encoder::Enqueue(ELF::Xword value) { + do { + const uint8_t byte = value & 127; + value >>= 7; + encoding_.push_back((value ? 128 : 0) | byte); + } while (value); +} + +// Add a vector of values to the encoding. +void Leb128Encoder::EnqueueAll(const std::vector& values) { + for (size_t i = 0; i < values.size(); ++i) + Enqueue(values[i]); +} + +// Create a new decoder for the given encoded stream. +Leb128Decoder::Leb128Decoder(const std::vector& encoding) { + encoding_ = encoding; + cursor_ = 0; +} + +// Empty destructor to silence chromium-style. +Leb128Decoder::~Leb128Decoder() { } + +// Decode and retrieve a single value from the encoding. Read forwards until +// a byte without its most significant bit is found, then read the 7 bit +// fields of the bytes spanned to re-form the value. +ELF::Xword Leb128Decoder::Dequeue() { + ELF::Xword value = 0; + + size_t shift = 0; + uint8_t byte; + + // Loop until we reach a byte with its high order bit clear. + do { + byte = encoding_[cursor_++]; + value |= static_cast(byte & 127) << shift; + shift += 7; + } while (byte & 128); + + return value; +} + +// Decode and retrieve all remaining values from the encoding. +void Leb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) + values->push_back(Dequeue()); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/leb128.h b/engine/src/flutter/tools/relocation_packer/src/leb128.h new file mode 100644 index 0000000000..6cc2d7caf8 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/leb128.h @@ -0,0 +1,74 @@ +// Copyright 2014 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. + +// LEB128 encoder and decoder for packed relative relocations. +// +// Run-length encoded relative relocations consist of a large number +// of pairs of relatively small positive integer values. Encoding these as +// LEB128 saves space. +// +// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ +#define TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ + +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode packed words as a LEB128 byte stream. +class Leb128Encoder { + public: + // Explicit (but empty) constructor and destructor, for chromium-style. + Leb128Encoder(); + ~Leb128Encoder(); + + // Add a value to the encoding stream. + // |value| is the unsigned int to add. + void Enqueue(ELF::Xword value); + + // Add a vector of values to the encoding stream. + // |values| is the vector of unsigned ints to add. + void EnqueueAll(const std::vector& values); + + // Retrieve the encoded representation of the values. + // |encoding| is the returned vector of encoded data. + void GetEncoding(std::vector* encoding) { *encoding = encoding_; } + + private: + // Growable vector holding the encoded LEB128 stream. + std::vector encoding_; +}; + +// Decode a LEB128 byte stream to produce packed words. +class Leb128Decoder { + public: + // Create a new decoder for the given encoded stream. + // |encoding| is the vector of encoded data. + explicit Leb128Decoder(const std::vector& encoding); + + // Explicit (but empty) destructor, for chromium-style. + ~Leb128Decoder(); + + // Retrieve the next value from the encoded stream. + ELF::Xword Dequeue(); + + // Retrieve all remaining values from the encoded stream. + // |values| is the vector of decoded data. + void DequeueAll(std::vector* values); + + private: + // Encoded LEB128 stream. + std::vector encoding_; + + // Cursor indicating the current stream retrieval point. + size_t cursor_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_LEB128_H_ diff --git a/engine/src/flutter/tools/relocation_packer/src/leb128_unittest.cc b/engine/src/flutter/tools/relocation_packer/src/leb128_unittest.cc new file mode 100644 index 0000000000..bd607b717b --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/leb128_unittest.cc @@ -0,0 +1,111 @@ +// Copyright 2014 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. + +#include "leb128.h" + +#include +#include "testing/gtest/include/gtest/gtest.h" + +namespace relocation_packer { + +TEST(Leb128, Encoder) { + std::vector values; + values.push_back(624485); + values.push_back(0); + values.push_back(1); + values.push_back(127); + values.push_back(128); + + Leb128Encoder encoder; + encoder.EnqueueAll(values); + + encoder.Enqueue(4294967295); + encoder.Enqueue(18446744073709551615ul); + + std::vector encoding; + encoder.GetEncoding(&encoding); + + EXPECT_EQ(23, encoding.size()); + // 624485 + EXPECT_EQ(0xe5, encoding[0]); + EXPECT_EQ(0x8e, encoding[1]); + EXPECT_EQ(0x26, encoding[2]); + // 0 + EXPECT_EQ(0x00, encoding[3]); + // 1 + EXPECT_EQ(0x01, encoding[4]); + // 127 + EXPECT_EQ(0x7f, encoding[5]); + // 128 + EXPECT_EQ(0x80, encoding[6]); + EXPECT_EQ(0x01, encoding[7]); + // 4294967295 + EXPECT_EQ(0xff, encoding[8]); + EXPECT_EQ(0xff, encoding[9]); + EXPECT_EQ(0xff, encoding[10]); + EXPECT_EQ(0xff, encoding[11]); + EXPECT_EQ(0x0f, encoding[12]); + // 18446744073709551615 + EXPECT_EQ(0xff, encoding[13]); + EXPECT_EQ(0xff, encoding[14]); + EXPECT_EQ(0xff, encoding[15]); + EXPECT_EQ(0xff, encoding[16]); + EXPECT_EQ(0xff, encoding[17]); + EXPECT_EQ(0xff, encoding[18]); + EXPECT_EQ(0xff, encoding[19]); + EXPECT_EQ(0xff, encoding[20]); + EXPECT_EQ(0xff, encoding[21]); + EXPECT_EQ(0x01, encoding[22]); +} + +TEST(Leb128, Decoder) { + std::vector encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 127 + encoding.push_back(0x7f); + // 128 + encoding.push_back(0x80); + encoding.push_back(0x01); + // 4294967295 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x0f); + // 18446744073709551615 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x01); + + Leb128Decoder decoder(encoding); + + EXPECT_EQ(624485, decoder.Dequeue()); + + std::vector dequeued; + decoder.DequeueAll(&dequeued); + + EXPECT_EQ(6, dequeued.size()); + EXPECT_EQ(0, dequeued[0]); + EXPECT_EQ(1, dequeued[1]); + EXPECT_EQ(127, dequeued[2]); + EXPECT_EQ(128, dequeued[3]); + EXPECT_EQ(4294967295, dequeued[4]); + EXPECT_EQ(18446744073709551615ul, dequeued[5]); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/main.cc b/engine/src/flutter/tools/relocation_packer/src/main.cc new file mode 100644 index 0000000000..28f5b04698 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/main.cc @@ -0,0 +1,175 @@ +// Copyright 2014 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. + +// Tool to pack and unpack relative relocations in a shared library. +// +// Packing removes relative relocations from .rel.dyn and writes them +// in a more compact form to .android.rel.dyn. Unpacking does the reverse. +// +// Invoke with -v to trace actions taken when packing or unpacking. +// Invoke with -p to pad removed relocations with R_*_NONE. Suppresses +// shrinking of .rel.dyn. +// See PrintUsage() below for full usage details. +// +// NOTE: Breaks with libelf 0.152, which is buggy. libelf 0.158 works. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "elf_file.h" +#include "libelf.h" + +namespace { + +void PrintUsage(const char* argv0) { + std::string temporary = argv0; + const size_t last_slash = temporary.find_last_of("/"); + if (last_slash != temporary.npos) { + temporary.erase(0, last_slash + 1); + } + const char* basename = temporary.c_str(); + + printf( + "Usage: %s [-u] [-v] [-p] file\n\n" + "Pack or unpack relative relocations in a shared library.\n\n" + " -u, --unpack unpack previously packed relative relocations\n" + " -v, --verbose trace object file modifications (for debugging)\n" + " -p, --pad do not shrink relocations, but pad (for debugging)\n\n", + basename); + + if (ELF::kMachine == EM_ARM) { + printf( + "Extracts relative relocations from the .rel.dyn section, packs them\n" + "into a more compact format, and stores the packed relocations in\n" + ".android.rel.dyn. Expands .android.rel.dyn to hold the packed\n" + "data, and shrinks .rel.dyn by the amount of unpacked data removed\n" + "from it.\n\n" + "Before being packed, a shared library needs to be prepared by adding\n" + "a null .android.rel.dyn section.\n\n" + "To pack relocations in a shared library:\n\n" + " echo -n 'NULL' >/tmp/small\n" + " arm-linux-androideabi-objcopy \\\n" + " --add-section .android.rel.dyn=/tmp/small \\\n" + " libchrome..so\n" + " rm /tmp/small\n" + " %s libchrome..so\n\n" + "To unpack and restore the shared library to its original state:\n\n" + " %s -u libchrome..so\n" + " arm-linux-androideabi-objcopy \\\n" + " --remove-section=.android.rel.dyn libchrome..so\n\n", + basename, basename); + } else if (ELF::kMachine == EM_AARCH64) { + printf( + "Extracts relative relocations from the .rela.dyn section, packs them\n" + "into a more compact format, and stores the packed relocations in\n" + ".android.rela.dyn. Expands .android.rela.dyn to hold the packed\n" + "data, and shrinks .rela.dyn by the amount of unpacked data removed\n" + "from it.\n\n" + "Before being packed, a shared library needs to be prepared by adding\n" + "a null .android.rela.dyn section.\n\n" + "To pack relocations in a shared library:\n\n" + " echo -n 'NULL' >/tmp/small\n" + " aarch64-linux-android-objcopy \\\n" + " --add-section .android.rela.dyn=/tmp/small \\\n" + " libchrome..so\n" + " rm /tmp/small\n" + " %s libchrome..so\n\n" + "To unpack and restore the shared library to its original state:\n\n" + " %s -u libchrome..so\n" + " aarch64-linux-android-objcopy \\\n" + " --remove-section=.android.rela.dyn libchrome..so\n\n", + basename, basename); + } else { + NOTREACHED(); + } + + printf( + "Debug sections are not handled, so packing should not be used on\n" + "shared libraries compiled for debugging or otherwise unstripped.\n"); +} + +} // namespace + +int main(int argc, char* argv[]) { + bool is_unpacking = false; + bool is_verbose = false; + bool is_padding = false; + + static const option options[] = { + {"unpack", 0, 0, 'u'}, {"verbose", 0, 0, 'v'}, {"pad", 0, 0, 'p'}, + {"help", 0, 0, 'h'}, {NULL, 0, 0, 0} + }; + bool has_options = true; + while (has_options) { + int c = getopt_long(argc, argv, "uvph", options, NULL); + switch (c) { + case 'u': + is_unpacking = true; + break; + case 'v': + is_verbose = true; + break; + case 'p': + is_padding = true; + break; + case 'h': + PrintUsage(argv[0]); + return 0; + case '?': + LOG(INFO) << "Try '" << argv[0] << " --help' for more information."; + return 1; + case -1: + has_options = false; + break; + default: + NOTREACHED(); + return 1; + } + } + if (optind != argc - 1) { + LOG(INFO) << "Try '" << argv[0] << " --help' for more information."; + return 1; + } + + if (elf_version(EV_CURRENT) == EV_NONE) { + LOG(WARNING) << "Elf Library is out of date!"; + } + + LOG(INFO) << "Configured for " << ELF::Machine(); + + const char* file = argv[argc - 1]; + const int fd = open(file, O_RDWR); + if (fd == -1) { + LOG(ERROR) << file << ": " << strerror(errno); + return 1; + } + + if (is_verbose) + relocation_packer::Logger::SetVerbose(1); + + relocation_packer::ElfFile elf_file(fd); + elf_file.SetPadding(is_padding); + + bool status; + if (is_unpacking) + status = elf_file.UnpackRelocations(); + else + status = elf_file.PackRelocations(); + + close(fd); + + if (!status) { + LOG(ERROR) << file << ": failed to pack/unpack file"; + return 1; + } + + return 0; +} diff --git a/engine/src/flutter/tools/relocation_packer/src/packer.cc b/engine/src/flutter/tools/relocation_packer/src/packer.cc new file mode 100644 index 0000000000..29bec1e3c6 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/packer.cc @@ -0,0 +1,124 @@ +// Copyright 2014 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. + +#include "packer.h" + +#include + +#include "debug.h" +#include "delta_encoder.h" +#include "elf_traits.h" +#include "leb128.h" +#include "run_length_encoder.h" +#include "sleb128.h" + +namespace relocation_packer { + +// Pack relative relocations into a run-length encoded packed +// representation. +void RelocationPacker::PackRelativeRelocations( + const std::vector& relocations, + std::vector* packed) { + // Run-length encode. + std::vector packed_words; + RelocationRunLengthCodec codec; + codec.Encode(relocations, &packed_words); + + // If insufficient data to run-length encode, do nothing. + if (packed_words.empty()) + return; + + // LEB128 encode, with "APR1" prefix. + Leb128Encoder encoder; + encoder.Enqueue('A'); + encoder.Enqueue('P'); + encoder.Enqueue('R'); + encoder.Enqueue('1'); + encoder.EnqueueAll(packed_words); + + encoder.GetEncoding(packed); + + // Pad packed to a whole number of words. This padding will decode as + // LEB128 zeroes. Run-length decoding ignores it because encoding + // embeds the pairs count in the stream itself. + while (packed->size() % sizeof(ELF::Word)) + packed->push_back(0); +} + +// Unpack relative relocations from a run-length encoded packed +// representation. +void RelocationPacker::UnpackRelativeRelocations( + const std::vector& packed, + std::vector* relocations) { + // LEB128 decode, after checking and stripping "APR1" prefix. + std::vector packed_words; + Leb128Decoder decoder(packed); + CHECK(decoder.Dequeue() == 'A' && + decoder.Dequeue() == 'P' && + decoder.Dequeue() == 'R' && + decoder.Dequeue() == '1'); + decoder.DequeueAll(&packed_words); + + // Run-length decode. + RelocationRunLengthCodec codec; + codec.Decode(packed_words, relocations); +} + +// Pack relative relocations with addends into a delta encoded packed +// representation. +void RelocationPacker::PackRelativeRelocations( + const std::vector& relocations, + std::vector* packed) { + // Delta encode. + std::vector packed_words; + RelocationDeltaCodec codec; + codec.Encode(relocations, &packed_words); + + // If insufficient data to delta encode, do nothing. + if (packed_words.empty()) + return; + + // Signed LEB128 encode, with "APA1" prefix. ASCII does not encode as + // itself under signed LEB128, so we have to treat it specially. + Sleb128Encoder encoder; + encoder.EnqueueAll(packed_words); + std::vector encoded; + encoder.GetEncoding(&encoded); + + packed->push_back('A'); + packed->push_back('P'); + packed->push_back('A'); + packed->push_back('1'); + packed->insert(packed->end(), encoded.begin(), encoded.end()); + + // Pad packed to a whole number of words. This padding will decode as + // signed LEB128 zeroes. Delta decoding ignores it because encoding + // embeds the pairs count in the stream itself. + while (packed->size() % sizeof(ELF::Word)) + packed->push_back(0); +} + +// Unpack relative relocations with addends from a delta encoded +// packed representation. +void RelocationPacker::UnpackRelativeRelocations( + const std::vector& packed, + std::vector* relocations) { + // Check "APA1" prefix. + CHECK(packed.at(0) == 'A' && + packed.at(1) == 'P' && + packed.at(2) == 'A' && + packed.at(3) == '1'); + + // Signed LEB128 decode, after stripping "APA1" prefix. + std::vector packed_words; + std::vector stripped(packed.begin() + 4, packed.end()); + Sleb128Decoder decoder(stripped); + decoder.DequeueAll(&packed_words); + + // Delta decode. + RelocationDeltaCodec codec; + codec.Decode(packed_words, relocations); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/packer.h b/engine/src/flutter/tools/relocation_packer/src/packer.h new file mode 100644 index 0000000000..db09ce8cc2 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/packer.h @@ -0,0 +1,78 @@ +// Copyright 2014 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. + +// Pack relative relocations into a more compact form. +// +// +// For relative relocations without addends (32 bit platforms) +// ----------------------------------------------------------- +// +// Applies two packing strategies. The first is run-length encoding, which +// turns a large set of relative relocations into a much smaller set +// of delta-count pairs, prefixed with a two-word header comprising the +// count of pairs and the initial relocation offset. The second is LEB128 +// encoding, which compresses the result of run-length encoding. +// +// Once packed, data is prefixed by an identifier that allows for any later +// versioning of packing strategies. +// +// A complete packed stream of relocations without addends might look +// something like: +// +// "APR1" pairs init_offset count1 delta1 count2 delta2 ... +// 41505231 f2b003 b08ac716 e001 04 01 10 ... +// +// +// For relative relocations with addends (64 bit platforms) +// -------------------------------------------------------- +// +// Applies two packing strategies. The first is delta encoding, which +// turns a large set of relative relocations into a smaller set +// of offset and addend delta pairs, prefixed with a header indicating the +// count of pairs. The second is signed LEB128 encoding, which compacts +// the result of delta encoding. +// +// Once packed, data is prefixed by an identifier that allows for any later +// versioning of packing strategies. +// +// A complete packed stream might look something like: +// +// "APA1" pairs offset_d1 addend_d1 offset_d2 addend_d2 ... +// 41505232 f2b018 04 28 08 9f01 ... + +#ifndef TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ +#define TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ + +#include +#include + +#include "elf.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// A RelocationPacker packs vectors of relative relocations into more +// compact forms, and unpacks them to reproduce the pre-packed data. +class RelocationPacker { + public: + // Pack relative relocations into a more compact form. + // |relocations| is a vector of relative relocation structs. + // |packed| is the vector of packed bytes into which relocations are packed. + static void PackRelativeRelocations(const std::vector& relocations, + std::vector* packed); + static void PackRelativeRelocations(const std::vector& relocations, + std::vector* packed); + + // Unpack relative relocations from their more compact form. + // |packed| is the vector of packed relocations. + // |relocations| is a vector of unpacked relative relocation structs. + static void UnpackRelativeRelocations(const std::vector& packed, + std::vector* relocations); + static void UnpackRelativeRelocations(const std::vector& packed, + std::vector* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_PACKER_H_ diff --git a/engine/src/flutter/tools/relocation_packer/src/packer_unittest.cc b/engine/src/flutter/tools/relocation_packer/src/packer_unittest.cc new file mode 100644 index 0000000000..de5be7979d --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/packer_unittest.cc @@ -0,0 +1,250 @@ +// Copyright 2014 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. + +#include "packer.h" + +#include +#include "elf.h" +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void AddRelocation(ELF::Addr addr, std::vector* relocations) { + ELF::Rel relocation; + relocation.r_offset = addr; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocations->push_back(relocation); +} + +bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) { + return relocation.r_offset == addr && + ELF_R_SYM(relocation.r_info) == 0 && + ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode; +} + +void AddRelocation(ELF::Addr addr, + ELF::Sxword addend, + std::vector* relocations) { + ELF::Rela relocation; + relocation.r_offset = addr; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocation.r_addend = addend; + relocations->push_back(relocation); +} + +bool CheckRelocation(ELF::Addr addr, + ELF::Sxword addend, + const ELF::Rela& relocation) { + return relocation.r_offset == addr && + ELF_R_SYM(relocation.r_info) == 0 && + ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode && + relocation.r_addend == addend; +} + +} // namespace + +namespace relocation_packer { + +TEST(Packer, PackRel) { + std::vector relocations; + std::vector packed; + + RelocationPacker packer; + + // Initial relocation. + AddRelocation(0xd1ce0000, &relocations); + // Two more relocations, 4 byte deltas. + AddRelocation(0xd1ce0004, &relocations); + AddRelocation(0xd1ce0008, &relocations); + // Three more relocations, 8 byte deltas. + AddRelocation(0xd1ce0010, &relocations); + AddRelocation(0xd1ce0018, &relocations); + AddRelocation(0xd1ce0020, &relocations); + + packed.clear(); + packer.PackRelativeRelocations(relocations, &packed); + + EXPECT_EQ(16, packed.size()); + // Identifier. + EXPECT_EQ('A', packed[0]); + EXPECT_EQ('P', packed[1]); + EXPECT_EQ('R', packed[2]); + EXPECT_EQ('1', packed[3]); + // Count-delta pairs count. + EXPECT_EQ(2, packed[4]); + // 0xd1ce0000 + EXPECT_EQ(128, packed[5]); + EXPECT_EQ(128, packed[6]); + EXPECT_EQ(184, packed[7]); + EXPECT_EQ(142, packed[8]); + EXPECT_EQ(13, packed[9]); + // Run of two relocations, 4 byte deltas. + EXPECT_EQ(2, packed[10]); + EXPECT_EQ(4, packed[11]); + // Run of three relocations, 8 byte deltas. + EXPECT_EQ(3, packed[12]); + EXPECT_EQ(8, packed[13]); + // Padding. + EXPECT_EQ(0, packed[14]); + EXPECT_EQ(0, packed[15]); +} + +TEST(Packer, UnpackRel) { + std::vector packed; + std::vector relocations; + + RelocationPacker packer; + + // Identifier. + packed.push_back('A'); + packed.push_back('P'); + packed.push_back('R'); + packed.push_back('1'); + // Count-delta pairs count. + packed.push_back(2); + // 0xd1ce0000 + packed.push_back(128); + packed.push_back(128); + packed.push_back(184); + packed.push_back(142); + packed.push_back(13); + // Run of two relocations, 4 byte deltas. + packed.push_back(2); + packed.push_back(4); + // Run of three relocations, 8 byte deltas. + packed.push_back(3); + packed.push_back(8); + // Padding. + packed.push_back(0); + packed.push_back(0); + + relocations.clear(); + packer.UnpackRelativeRelocations(packed, &relocations); + + EXPECT_EQ(6, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xd1ce0000, relocations[0])); + // Two relocations, 4 byte deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0004, relocations[1])); + EXPECT_TRUE(CheckRelocation(0xd1ce0008, relocations[2])); + // Three relocations, 8 byte deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0010, relocations[3])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, relocations[4])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, relocations[5])); +} + +TEST(Packer, PackRela) { + std::vector relocations; + std::vector packed; + + RelocationPacker packer; + + // Initial relocation. + AddRelocation(0xd1ce0000, 10000, &relocations); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + AddRelocation(0xd1ce0004, 10012, &relocations); + AddRelocation(0xd1ce0008, 10024, &relocations); + // Three more relocations, 8 byte deltas, -24 byte addend deltas. + AddRelocation(0xd1ce0010, 10000, &relocations); + AddRelocation(0xd1ce0018, 9976, &relocations); + AddRelocation(0xd1ce0020, 9952, &relocations); + + packed.clear(); + packer.PackRelativeRelocations(relocations, &packed); + + EXPECT_EQ(24, packed.size()); + // Identifier. + EXPECT_EQ('A', packed[0]); + EXPECT_EQ('P', packed[1]); + EXPECT_EQ('A', packed[2]); + EXPECT_EQ('1', packed[3]); + // Delta pairs count. + EXPECT_EQ(6, packed[4]); + // 0xd1ce0000 + EXPECT_EQ(128, packed[5]); + EXPECT_EQ(128, packed[6]); + EXPECT_EQ(184, packed[7]); + EXPECT_EQ(142, packed[8]); + EXPECT_EQ(13, packed[9]); + // 10000 + EXPECT_EQ(144, packed[10]); + EXPECT_EQ(206, packed[11]); + EXPECT_EQ(0, packed[12]); + // 4, 12 + EXPECT_EQ(4, packed[13]); + EXPECT_EQ(12, packed[14]); + // 4, 12 + EXPECT_EQ(4, packed[15]); + EXPECT_EQ(12, packed[16]); + // 8, -24 + EXPECT_EQ(8, packed[17]); + EXPECT_EQ(104, packed[18]); + // 8, -24 + EXPECT_EQ(8, packed[19]); + EXPECT_EQ(104, packed[20]); + // 8, -24 + EXPECT_EQ(8, packed[21]); + EXPECT_EQ(104, packed[22]); + // Padding. + EXPECT_EQ(0, packed[23]); +} + +TEST(Packer, UnpackRela) { + std::vector packed; + std::vector relocations; + + RelocationPacker packer; + + // Identifier. + packed.push_back('A'); + packed.push_back('P'); + packed.push_back('A'); + packed.push_back('1'); + // Delta pairs count. + packed.push_back(6); + // 0xd1ce0000 + packed.push_back(128); + packed.push_back(128); + packed.push_back(184); + packed.push_back(142); + packed.push_back(13); + // 10000 + packed.push_back(144); + packed.push_back(206); + packed.push_back(0); + // 4, 12 + packed.push_back(4); + packed.push_back(12); + // 4, 12 + packed.push_back(4); + packed.push_back(12); + // 8, -24 + packed.push_back(8); + packed.push_back(104); + // 8, -24 + packed.push_back(8); + packed.push_back(104); + // 8, -24 + packed.push_back(8); + packed.push_back(104); + // Padding. + packed.push_back(0); + + relocations.clear(); + packer.UnpackRelativeRelocations(packed, &relocations); + + EXPECT_EQ(6, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xd1ce0000, 10000, relocations[0])); + // Two more relocations, 4 byte offset deltas, 12 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0004, 10012, relocations[1])); + EXPECT_TRUE(CheckRelocation(0xd1ce0008, 10024, relocations[2])); + // Three more relocations, 8 byte offset deltas, -24 byte addend deltas. + EXPECT_TRUE(CheckRelocation(0xd1ce0010, 10000, relocations[3])); + EXPECT_TRUE(CheckRelocation(0xd1ce0018, 9976, relocations[4])); + EXPECT_TRUE(CheckRelocation(0xd1ce0020, 9952, relocations[5])); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/run_all_unittests.cc b/engine/src/flutter/tools/relocation_packer/src/run_all_unittests.cc new file mode 100644 index 0000000000..4122be18ce --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/run_all_unittests.cc @@ -0,0 +1,10 @@ +// Copyright 2014 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. + +#include "testing/gtest/include/gtest/gtest.h" + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/engine/src/flutter/tools/relocation_packer/src/run_length_encoder.cc b/engine/src/flutter/tools/relocation_packer/src/run_length_encoder.cc new file mode 100644 index 0000000000..2f2e1c315f --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/run_length_encoder.cc @@ -0,0 +1,144 @@ +// Copyright 2014 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. + +#include "run_length_encoder.h" + +#include + +#include "debug.h" +#include "elf_traits.h" + +namespace relocation_packer { + +namespace { + +// Generate a vector of deltas between the r_offset fields of adjacent +// relative relocations. +void GetDeltas(const std::vector& relocations, + std::vector* deltas) { + CHECK(relocations.size() >= 2); + + for (size_t i = 0; i < relocations.size() - 1; ++i) { + const ELF::Rel* first = &relocations[i]; + CHECK(ELF_R_TYPE(first->r_info) == ELF::kRelativeRelocationCode); + + const ELF::Rel* second = &relocations[i + 1]; + CHECK(ELF_R_TYPE(second->r_info) == ELF::kRelativeRelocationCode); + + // Requires that offsets are 'strictly increasing'. The packing + // algorithm fails if this does not hold. + CHECK(second->r_offset > first->r_offset); + deltas->push_back(second->r_offset - first->r_offset); + } +} + +// Condense a set of r_offset deltas into a run-length encoded packing. +// Represented as count-delta pairs, where count is the run length and +// delta the common difference between adjacent r_offsets. +void Condense(const std::vector& deltas, + std::vector* packed) { + CHECK(!deltas.empty()); + size_t count = 0; + ELF::Addr current = deltas[0]; + + // Identify spans of identically valued deltas. + for (size_t i = 0; i < deltas.size(); ++i) { + const ELF::Addr delta = deltas[i]; + if (delta == current) { + count++; + } else { + // We reached the end of a span of identically valued deltas. + packed->push_back(count); + packed->push_back(current); + current = delta; + count = 1; + } + } + + // Write the final span. + packed->push_back(count); + packed->push_back(current); +} + +// Uncondense a set of r_offset deltas from a run-length encoded packing. +// The initial address for uncondensing, the start index for the first +// condensed slot in packed, and the count of pairs are provided. +void Uncondense(ELF::Addr addr, + const std::vector& packed, + size_t start_index, + size_t end_index, + std::vector* relocations) { + // The first relocation is just one created from the initial address. + ELF::Rel initial; + initial.r_offset = addr; + initial.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocations->push_back(initial); + + // Read each count and delta pair, beginning at the start index and + // finishing at the end index. + for (size_t i = start_index; i < end_index; i += 2) { + size_t count = packed[i]; + const ELF::Addr delta = packed[i + 1]; + CHECK(count > 0 && delta > 0); + + // Generate relocations for this count and delta pair. + while (count) { + addr += delta; + ELF::Rel relocation; + relocation.r_offset = addr; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocations->push_back(relocation); + count--; + } + } +} + +} // namespace + +// Encode relative relocations into a run-length encoded (packed) +// representation. +void RelocationRunLengthCodec::Encode(const std::vector& relocations, + std::vector* packed) { + // If we have zero or one relocation only then there is no packing + // possible; a run-length encoding needs a run. + if (relocations.size() < 2) + return; + + std::vector deltas; + GetDeltas(relocations, &deltas); + + // Reserve space for the element count. + packed->push_back(0); + + // Initialize the packed data with the first offset, then follow up with + // the condensed deltas vector. + packed->push_back(relocations[0].r_offset); + Condense(deltas, packed); + + // Fill in the packed pair count. + packed->at(0) = (packed->size() - 2) >> 1; +} + +// Decode relative relocations from a run-length encoded (packed) +// representation. +void RelocationRunLengthCodec::Decode(const std::vector& packed, + std::vector* relocations) { + // We need at least one packed pair after the packed pair count and start + // address to be able to unpack. + if (packed.size() < 4) + return; + + // Ensure that the packed data offers enough pairs. There may be zero + // padding on it that we ignore. + CHECK(packed[0] <= (packed.size() - 2) >> 1); + + // The first packed vector element is the pairs count and the second the + // initial address. Start uncondensing pairs at the third, and finish + // at the end of the pairs data. + const size_t pairs_count = packed[0]; + const ELF::Addr addr = packed[1]; + Uncondense(addr, packed, 2, 2 + (pairs_count << 1), relocations); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/run_length_encoder.h b/engine/src/flutter/tools/relocation_packer/src/run_length_encoder.h new file mode 100644 index 0000000000..f3a80e6025 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/run_length_encoder.h @@ -0,0 +1,81 @@ +// Copyright 2014 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. + +// Run-length encode and decode relative relocations. +// +// Relative relocations are the bulk of dynamic relocations (the +// .rel.dyn or .rela.dyn sections) in libchrome..so, and the ELF +// standard representation of them is wasteful. .rel.dyn contains +// relocations without addends, .rela.dyn relocations with addends. +// +// A relocation with no addend is 8 bytes on 32 bit platforms and 16 bytes +// on 64 bit plaforms, split into offset and info fields. Offsets strictly +// increase, and each is commonly a few bytes different from its predecessor. +// There are long runs where the difference does not change. The info field +// is constant. Example, from 'readelf -x4 libchrome..so' 32 bit: +// +// offset info offset info +// 808fef01 17000000 848fef01 17000000 ................ +// 888fef01 17000000 8c8fef01 17000000 ................ +// 908fef01 17000000 948fef01 17000000 ................ +// +// Run length encoding packs this data more efficiently, by representing it +// as a delta and a count of entries each differing from its predecessor +// by this delta. The above can be represented as a start address followed +// by an encoded count of 6 and offset difference of 4: +// +// start count diff +// 01ef8f80 00000006 00000004 +// +// Because relative relocation offsets strictly increase, the complete +// set of relative relocations in libchrome..so can be +// represented by a single start address followed by one or more difference +// and count encoded word pairs: +// +// start run1 count run1 diff run2 count run2 diff +// 01ef8f80 00000006 00000004 00000010 00000008 ... +// +// Decoding regenerates relative relocations beginning at address +// 'start' and for each encoded run, incrementing the address by 'difference' +// for 'count' iterations and emitting a new relative relocation. +// +// Once encoded, data is prefixed by a single word count of packed delta and +// count pairs. A final run-length encoded relative relocations vector +// might therefore look something like: +// +// pairs start run 1 run 2 ... run 15 +// 0000000f 01ef8f80 00000006 00000004 00000010 00000008 ... +// Interpreted as: +// pairs=15 start=.. count=6,delta=4 count=16,delta=8 + +#ifndef TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ +#define TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ + +#include + +#include "elf.h" +#include "elf_traits.h" + +namespace relocation_packer { + +// A RelocationRunLengthCodec packs vectors of relative relocations +// into more compact forms, and unpacks them to reproduce the pre-packed data. +class RelocationRunLengthCodec { + public: + // Encode relative relocations into a more compact form. + // |relocations| is a vector of relative relocation structs. + // |packed| is the vector of packed words into which relocations are packed. + static void Encode(const std::vector& relocations, + std::vector* packed); + + // Decode relative relocations from their more compact form. + // |packed| is the vector of packed relocations. + // |relocations| is a vector of unpacked relative relocation structs. + static void Decode(const std::vector& packed, + std::vector* relocations); +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_RUN_LENGTH_ENCODER_H_ diff --git a/engine/src/flutter/tools/relocation_packer/src/run_length_encoder_unittest.cc b/engine/src/flutter/tools/relocation_packer/src/run_length_encoder_unittest.cc new file mode 100644 index 0000000000..83370f2a97 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/run_length_encoder_unittest.cc @@ -0,0 +1,124 @@ +// Copyright 2014 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. + +#include "run_length_encoder.h" + +#include +#include "elf.h" +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void AddRelocation(ELF::Addr addr, std::vector* relocations) { + ELF::Rel relocation; + relocation.r_offset = addr; + relocation.r_info = ELF_R_INFO(0, ELF::kRelativeRelocationCode); + relocations->push_back(relocation); +} + +bool CheckRelocation(ELF::Addr addr, const ELF::Rel& relocation) { + return relocation.r_offset == addr && + ELF_R_SYM(relocation.r_info) == 0 && + ELF_R_TYPE(relocation.r_info) == ELF::kRelativeRelocationCode; +} + +} // namespace + +namespace relocation_packer { + +TEST(RunLength, Encode) { + std::vector relocations; + std::vector packed; + + RelocationRunLengthCodec codec; + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(0, packed.size()); + + // Add one relocation (insufficient data to encode). + AddRelocation(0xf00d0000, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(0, packed.size()); + + // Add a second relocation, 4 byte delta (minimum data to encode). + AddRelocation(0xf00d0004, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(4, packed.size()); + // One count-delta pair present. + EXPECT_EQ(1, packed[0]); + // Initial relocation. + EXPECT_EQ(0xf00d0000, packed[1]); + // Run of a single relocation, 4 byte delta. + EXPECT_EQ(1, packed[2]); + EXPECT_EQ(4, packed[3]); + + // Add a third relocation, 4 byte delta. + AddRelocation(0xf00d0008, &relocations); + + // Add three more relocations, 8 byte deltas. + AddRelocation(0xf00d0010, &relocations); + AddRelocation(0xf00d0018, &relocations); + AddRelocation(0xf00d0020, &relocations); + + packed.clear(); + codec.Encode(relocations, &packed); + + EXPECT_EQ(6, packed.size()); + // Two count-delta pairs present. + EXPECT_EQ(2, packed[0]); + // Initial relocation. + EXPECT_EQ(0xf00d0000, packed[1]); + // Run of two relocations, 4 byte deltas. + EXPECT_EQ(2, packed[2]); + EXPECT_EQ(4, packed[3]); + // Run of three relocations, 8 byte deltas. + EXPECT_EQ(3, packed[4]); + EXPECT_EQ(8, packed[5]); +} + +TEST(RunLength, Decode) { + std::vector packed; + std::vector relocations; + + RelocationRunLengthCodec codec; + codec.Decode(packed, &relocations); + + EXPECT_EQ(0, relocations.size()); + + // Two count-delta pairs. + packed.push_back(2); + // Initial relocation. + packed.push_back(0xc0de0000); + // Run of two relocations, 4 byte deltas. + packed.push_back(2); + packed.push_back(4); + // Run of three relocations, 8 byte deltas. + packed.push_back(3); + packed.push_back(8); + + relocations.clear(); + codec.Decode(packed, &relocations); + + EXPECT_EQ(6, relocations.size()); + // Initial relocation. + EXPECT_TRUE(CheckRelocation(0xc0de0000, relocations[0])); + // Two relocations, 4 byte deltas. + EXPECT_TRUE(CheckRelocation(0xc0de0004, relocations[1])); + EXPECT_TRUE(CheckRelocation(0xc0de0008, relocations[2])); + // Three relocations, 8 byte deltas. + EXPECT_TRUE(CheckRelocation(0xc0de0010, relocations[3])); + EXPECT_TRUE(CheckRelocation(0xc0de0018, relocations[4])); + EXPECT_TRUE(CheckRelocation(0xc0de0020, relocations[5])); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/sleb128.cc b/engine/src/flutter/tools/relocation_packer/src/sleb128.cc new file mode 100644 index 0000000000..a10bd79a71 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/sleb128.cc @@ -0,0 +1,95 @@ +// Copyright 2014 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. + +#include "sleb128.h" + +#include +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Empty constructor and destructor to silence chromium-style. +Sleb128Encoder::Sleb128Encoder() { } +Sleb128Encoder::~Sleb128Encoder() { } + +// Add a single value to the encoding. Values are encoded with variable +// length. The least significant 7 bits of each byte hold 7 bits of data, +// and the most significant bit is set on each byte except the last. The +// value is sign extended up to a multiple of 7 bits (ensuring that the +// most significant bit is zero for a positive number and one for a +// negative number). +void Sleb128Encoder::Enqueue(ELF::Sxword value) { + static const size_t size = CHAR_BIT * sizeof(value); + + bool more = true; + const bool negative = value < 0; + + while (more) { + uint8_t byte = value & 127; + value >>= 7; + + // Sign extend if encoding a -ve value. + if (negative) + value |= -(static_cast(1) << (size - 7)); + + // The sign bit of byte is second high order bit. + const bool sign_bit = byte & 64; + if ((value == 0 && !sign_bit) || (value == -1 && sign_bit)) + more = false; + else + byte |= 128; + encoding_.push_back(byte); + } +} + +// Add a vector of values to the encoding. +void Sleb128Encoder::EnqueueAll(const std::vector& values) { + for (size_t i = 0; i < values.size(); ++i) + Enqueue(values[i]); +} + +// Create a new decoder for the given encoded stream. +Sleb128Decoder::Sleb128Decoder(const std::vector& encoding) { + encoding_ = encoding; + cursor_ = 0; +} + +// Empty destructor to silence chromium-style. +Sleb128Decoder::~Sleb128Decoder() { } + +// Decode and retrieve a single value from the encoding. Consume bytes +// until one without its most significant bit is found, and re-form the +// value from the 7 bit fields of the bytes consumed. +ELF::Sxword Sleb128Decoder::Dequeue() { + ELF::Sxword value = 0; + static const size_t size = CHAR_BIT * sizeof(value); + + size_t shift = 0; + uint8_t byte; + + // Loop until we reach a byte with its high order bit clear. + do { + byte = encoding_[cursor_++]; + value |= (static_cast(byte & 127) << shift); + shift += 7; + } while (byte & 128); + + // The sign bit is second high order bit of the final byte decoded. + // Sign extend if value is -ve and we did not shift all of it. + if (shift < size && (byte & 64)) + value |= -(static_cast(1) << shift); + + return value; +} + +// Decode and retrieve all remaining values from the encoding. +void Sleb128Decoder::DequeueAll(std::vector* values) { + while (cursor_ < encoding_.size()) + values->push_back(Dequeue()); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/src/sleb128.h b/engine/src/flutter/tools/relocation_packer/src/sleb128.h new file mode 100644 index 0000000000..3544543c0d --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/sleb128.h @@ -0,0 +1,75 @@ +// Copyright 2014 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. + +// SLEB128 encoder and decoder for packed relative relocations. +// +// Delta encoded relative relocations consist of a large number +// of pairs signed integer values, many with small values. Encoding these +// as signed LEB128 saves space. +// +// For more on LEB128 see http://en.wikipedia.org/wiki/LEB128. + +#ifndef TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ +#define TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ + +#include +#include +#include + +#include "elf_traits.h" + +namespace relocation_packer { + +// Encode packed words as a signed LEB128 byte stream. +class Sleb128Encoder { + public: + // Explicit (but empty) constructor and destructor, for chromium-style. + Sleb128Encoder(); + ~Sleb128Encoder(); + + // Add a value to the encoding stream. + // |value| is the signed int to add. + void Enqueue(ELF::Sxword value); + + // Add a vector of values to the encoding stream. + // |values| is the vector of signed ints to add. + void EnqueueAll(const std::vector& values); + + // Retrieve the encoded representation of the values. + // |encoding| is the returned vector of encoded data. + void GetEncoding(std::vector* encoding) { *encoding = encoding_; } + + private: + // Growable vector holding the encoded LEB128 stream. + std::vector encoding_; +}; + +// Decode a LEB128 byte stream to produce packed words. +class Sleb128Decoder { + public: + // Create a new decoder for the given encoded stream. + // |encoding| is the vector of encoded data. + explicit Sleb128Decoder(const std::vector& encoding); + + // Explicit (but empty) destructor, for chromium-style. + ~Sleb128Decoder(); + + // Retrieve the next value from the encoded stream. + ELF::Sxword Dequeue(); + + // Retrieve all remaining values from the encoded stream. + // |values| is the vector of decoded data. + void DequeueAll(std::vector* values); + + private: + // Encoded LEB128 stream. + std::vector encoding_; + + // Cursor indicating the current stream retrieval point. + size_t cursor_; +}; + +} // namespace relocation_packer + +#endif // TOOLS_RELOCATION_PACKER_SRC_SLEB128_H_ diff --git a/engine/src/flutter/tools/relocation_packer/src/sleb128_unittest.cc b/engine/src/flutter/tools/relocation_packer/src/sleb128_unittest.cc new file mode 100644 index 0000000000..60a5d0de71 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/src/sleb128_unittest.cc @@ -0,0 +1,166 @@ +// Copyright 2014 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. + +#include "sleb128.h" + +#include +#include "elf_traits.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace relocation_packer { + +TEST(Sleb128, Encoder) { + std::vector values; + values.push_back(624485); + values.push_back(0); + values.push_back(1); + values.push_back(63); + values.push_back(64); + values.push_back(-1); + values.push_back(-624485); + + Sleb128Encoder encoder; + encoder.EnqueueAll(values); + + encoder.Enqueue(2147483647); + encoder.Enqueue(-2147483648); + encoder.Enqueue(9223372036854775807ll); + encoder.Enqueue(-9223372036854775807ll - 1); + + std::vector encoding; + encoder.GetEncoding(&encoding); + + EXPECT_EQ(42u, encoding.size()); + // 624485 + EXPECT_EQ(0xe5, encoding[0]); + EXPECT_EQ(0x8e, encoding[1]); + EXPECT_EQ(0x26, encoding[2]); + // 0 + EXPECT_EQ(0x00, encoding[3]); + // 1 + EXPECT_EQ(0x01, encoding[4]); + // 63 + EXPECT_EQ(0x3f, encoding[5]); + // 64 + EXPECT_EQ(0xc0, encoding[6]); + EXPECT_EQ(0x00, encoding[7]); + // -1 + EXPECT_EQ(0x7f, encoding[8]); + // -624485 + EXPECT_EQ(0x9b, encoding[9]); + EXPECT_EQ(0xf1, encoding[10]); + EXPECT_EQ(0x59, encoding[11]); + // 2147483647 + EXPECT_EQ(0xff, encoding[12]); + EXPECT_EQ(0xff, encoding[13]); + EXPECT_EQ(0xff, encoding[14]); + EXPECT_EQ(0xff, encoding[15]); + EXPECT_EQ(0x07, encoding[16]); + // -2147483648 + EXPECT_EQ(0x80, encoding[17]); + EXPECT_EQ(0x80, encoding[18]); + EXPECT_EQ(0x80, encoding[19]); + EXPECT_EQ(0x80, encoding[20]); + EXPECT_EQ(0x78, encoding[21]); + // 9223372036854775807 + EXPECT_EQ(0xff, encoding[22]); + EXPECT_EQ(0xff, encoding[23]); + EXPECT_EQ(0xff, encoding[24]); + EXPECT_EQ(0xff, encoding[25]); + EXPECT_EQ(0xff, encoding[26]); + EXPECT_EQ(0xff, encoding[27]); + EXPECT_EQ(0xff, encoding[28]); + EXPECT_EQ(0xff, encoding[29]); + EXPECT_EQ(0xff, encoding[30]); + EXPECT_EQ(0x00, encoding[31]); + // -9223372036854775808 + EXPECT_EQ(0x80, encoding[32]); + EXPECT_EQ(0x80, encoding[33]); + EXPECT_EQ(0x80, encoding[34]); + EXPECT_EQ(0x80, encoding[35]); + EXPECT_EQ(0x80, encoding[36]); + EXPECT_EQ(0x80, encoding[37]); + EXPECT_EQ(0x80, encoding[38]); + EXPECT_EQ(0x80, encoding[39]); + EXPECT_EQ(0x80, encoding[40]); + EXPECT_EQ(0x7f, encoding[41]); +} + +TEST(Sleb128, Decoder) { + std::vector encoding; + // 624485 + encoding.push_back(0xe5); + encoding.push_back(0x8e); + encoding.push_back(0x26); + // 0 + encoding.push_back(0x00); + // 1 + encoding.push_back(0x01); + // 63 + encoding.push_back(0x3f); + // 64 + encoding.push_back(0xc0); + encoding.push_back(0x00); + // -1 + encoding.push_back(0x7f); + // -624485 + encoding.push_back(0x9b); + encoding.push_back(0xf1); + encoding.push_back(0x59); + // 2147483647 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x07); + // -2147483648 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x78); + // 9223372036854775807 + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0xff); + encoding.push_back(0x00); + // -9223372036854775808 + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x80); + encoding.push_back(0x7f); + + Sleb128Decoder decoder(encoding); + + EXPECT_EQ(624485, decoder.Dequeue()); + + std::vector dequeued; + decoder.DequeueAll(&dequeued); + + EXPECT_EQ(10u, dequeued.size()); + EXPECT_EQ(0, dequeued[0]); + EXPECT_EQ(1, dequeued[1]); + EXPECT_EQ(63, dequeued[2]); + EXPECT_EQ(64, dequeued[3]); + EXPECT_EQ(-1, dequeued[4]); + EXPECT_EQ(-624485, dequeued[5]); + EXPECT_EQ(2147483647, dequeued[6]); + EXPECT_EQ(-2147483648, dequeued[7]); + EXPECT_EQ(9223372036854775807ll, dequeued[8]); + EXPECT_EQ(-9223372036854775807ll - 1, dequeued[9]); +} + +} // namespace relocation_packer diff --git a/engine/src/flutter/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc b/engine/src/flutter/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc new file mode 100644 index 0000000000..5e1fa747e8 --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/test_data/elf_file_unittest_relocs.cc @@ -0,0 +1,1014 @@ +// Copyright 2014 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. + +// Test data for packing/unpacking. When compiled, creates a run of +// relative relocations. +// +// See generate_elf_file_unittest_relocs.sh for instructions on how to build +// unit test data from this source file. + +const int i = 0; + +// Generator: +// python -c 'for i in xrange(0,1000):print"const void* pointer_%d = &i;"%i' +const void* pointer_0 = &i; +const void* pointer_1 = &i; +const void* pointer_2 = &i; +const void* pointer_3 = &i; +const void* pointer_4 = &i; +const void* pointer_5 = &i; +const void* pointer_6 = &i; +const void* pointer_7 = &i; +const void* pointer_8 = &i; +const void* pointer_9 = &i; +const void* pointer_10 = &i; +const void* pointer_11 = &i; +const void* pointer_12 = &i; +const void* pointer_13 = &i; +const void* pointer_14 = &i; +const void* pointer_15 = &i; +const void* pointer_16 = &i; +const void* pointer_17 = &i; +const void* pointer_18 = &i; +const void* pointer_19 = &i; +const void* pointer_20 = &i; +const void* pointer_21 = &i; +const void* pointer_22 = &i; +const void* pointer_23 = &i; +const void* pointer_24 = &i; +const void* pointer_25 = &i; +const void* pointer_26 = &i; +const void* pointer_27 = &i; +const void* pointer_28 = &i; +const void* pointer_29 = &i; +const void* pointer_30 = &i; +const void* pointer_31 = &i; +const void* pointer_32 = &i; +const void* pointer_33 = &i; +const void* pointer_34 = &i; +const void* pointer_35 = &i; +const void* pointer_36 = &i; +const void* pointer_37 = &i; +const void* pointer_38 = &i; +const void* pointer_39 = &i; +const void* pointer_40 = &i; +const void* pointer_41 = &i; +const void* pointer_42 = &i; +const void* pointer_43 = &i; +const void* pointer_44 = &i; +const void* pointer_45 = &i; +const void* pointer_46 = &i; +const void* pointer_47 = &i; +const void* pointer_48 = &i; +const void* pointer_49 = &i; +const void* pointer_50 = &i; +const void* pointer_51 = &i; +const void* pointer_52 = &i; +const void* pointer_53 = &i; +const void* pointer_54 = &i; +const void* pointer_55 = &i; +const void* pointer_56 = &i; +const void* pointer_57 = &i; +const void* pointer_58 = &i; +const void* pointer_59 = &i; +const void* pointer_60 = &i; +const void* pointer_61 = &i; +const void* pointer_62 = &i; +const void* pointer_63 = &i; +const void* pointer_64 = &i; +const void* pointer_65 = &i; +const void* pointer_66 = &i; +const void* pointer_67 = &i; +const void* pointer_68 = &i; +const void* pointer_69 = &i; +const void* pointer_70 = &i; +const void* pointer_71 = &i; +const void* pointer_72 = &i; +const void* pointer_73 = &i; +const void* pointer_74 = &i; +const void* pointer_75 = &i; +const void* pointer_76 = &i; +const void* pointer_77 = &i; +const void* pointer_78 = &i; +const void* pointer_79 = &i; +const void* pointer_80 = &i; +const void* pointer_81 = &i; +const void* pointer_82 = &i; +const void* pointer_83 = &i; +const void* pointer_84 = &i; +const void* pointer_85 = &i; +const void* pointer_86 = &i; +const void* pointer_87 = &i; +const void* pointer_88 = &i; +const void* pointer_89 = &i; +const void* pointer_90 = &i; +const void* pointer_91 = &i; +const void* pointer_92 = &i; +const void* pointer_93 = &i; +const void* pointer_94 = &i; +const void* pointer_95 = &i; +const void* pointer_96 = &i; +const void* pointer_97 = &i; +const void* pointer_98 = &i; +const void* pointer_99 = &i; +const void* pointer_100 = &i; +const void* pointer_101 = &i; +const void* pointer_102 = &i; +const void* pointer_103 = &i; +const void* pointer_104 = &i; +const void* pointer_105 = &i; +const void* pointer_106 = &i; +const void* pointer_107 = &i; +const void* pointer_108 = &i; +const void* pointer_109 = &i; +const void* pointer_110 = &i; +const void* pointer_111 = &i; +const void* pointer_112 = &i; +const void* pointer_113 = &i; +const void* pointer_114 = &i; +const void* pointer_115 = &i; +const void* pointer_116 = &i; +const void* pointer_117 = &i; +const void* pointer_118 = &i; +const void* pointer_119 = &i; +const void* pointer_120 = &i; +const void* pointer_121 = &i; +const void* pointer_122 = &i; +const void* pointer_123 = &i; +const void* pointer_124 = &i; +const void* pointer_125 = &i; +const void* pointer_126 = &i; +const void* pointer_127 = &i; +const void* pointer_128 = &i; +const void* pointer_129 = &i; +const void* pointer_130 = &i; +const void* pointer_131 = &i; +const void* pointer_132 = &i; +const void* pointer_133 = &i; +const void* pointer_134 = &i; +const void* pointer_135 = &i; +const void* pointer_136 = &i; +const void* pointer_137 = &i; +const void* pointer_138 = &i; +const void* pointer_139 = &i; +const void* pointer_140 = &i; +const void* pointer_141 = &i; +const void* pointer_142 = &i; +const void* pointer_143 = &i; +const void* pointer_144 = &i; +const void* pointer_145 = &i; +const void* pointer_146 = &i; +const void* pointer_147 = &i; +const void* pointer_148 = &i; +const void* pointer_149 = &i; +const void* pointer_150 = &i; +const void* pointer_151 = &i; +const void* pointer_152 = &i; +const void* pointer_153 = &i; +const void* pointer_154 = &i; +const void* pointer_155 = &i; +const void* pointer_156 = &i; +const void* pointer_157 = &i; +const void* pointer_158 = &i; +const void* pointer_159 = &i; +const void* pointer_160 = &i; +const void* pointer_161 = &i; +const void* pointer_162 = &i; +const void* pointer_163 = &i; +const void* pointer_164 = &i; +const void* pointer_165 = &i; +const void* pointer_166 = &i; +const void* pointer_167 = &i; +const void* pointer_168 = &i; +const void* pointer_169 = &i; +const void* pointer_170 = &i; +const void* pointer_171 = &i; +const void* pointer_172 = &i; +const void* pointer_173 = &i; +const void* pointer_174 = &i; +const void* pointer_175 = &i; +const void* pointer_176 = &i; +const void* pointer_177 = &i; +const void* pointer_178 = &i; +const void* pointer_179 = &i; +const void* pointer_180 = &i; +const void* pointer_181 = &i; +const void* pointer_182 = &i; +const void* pointer_183 = &i; +const void* pointer_184 = &i; +const void* pointer_185 = &i; +const void* pointer_186 = &i; +const void* pointer_187 = &i; +const void* pointer_188 = &i; +const void* pointer_189 = &i; +const void* pointer_190 = &i; +const void* pointer_191 = &i; +const void* pointer_192 = &i; +const void* pointer_193 = &i; +const void* pointer_194 = &i; +const void* pointer_195 = &i; +const void* pointer_196 = &i; +const void* pointer_197 = &i; +const void* pointer_198 = &i; +const void* pointer_199 = &i; +const void* pointer_200 = &i; +const void* pointer_201 = &i; +const void* pointer_202 = &i; +const void* pointer_203 = &i; +const void* pointer_204 = &i; +const void* pointer_205 = &i; +const void* pointer_206 = &i; +const void* pointer_207 = &i; +const void* pointer_208 = &i; +const void* pointer_209 = &i; +const void* pointer_210 = &i; +const void* pointer_211 = &i; +const void* pointer_212 = &i; +const void* pointer_213 = &i; +const void* pointer_214 = &i; +const void* pointer_215 = &i; +const void* pointer_216 = &i; +const void* pointer_217 = &i; +const void* pointer_218 = &i; +const void* pointer_219 = &i; +const void* pointer_220 = &i; +const void* pointer_221 = &i; +const void* pointer_222 = &i; +const void* pointer_223 = &i; +const void* pointer_224 = &i; +const void* pointer_225 = &i; +const void* pointer_226 = &i; +const void* pointer_227 = &i; +const void* pointer_228 = &i; +const void* pointer_229 = &i; +const void* pointer_230 = &i; +const void* pointer_231 = &i; +const void* pointer_232 = &i; +const void* pointer_233 = &i; +const void* pointer_234 = &i; +const void* pointer_235 = &i; +const void* pointer_236 = &i; +const void* pointer_237 = &i; +const void* pointer_238 = &i; +const void* pointer_239 = &i; +const void* pointer_240 = &i; +const void* pointer_241 = &i; +const void* pointer_242 = &i; +const void* pointer_243 = &i; +const void* pointer_244 = &i; +const void* pointer_245 = &i; +const void* pointer_246 = &i; +const void* pointer_247 = &i; +const void* pointer_248 = &i; +const void* pointer_249 = &i; +const void* pointer_250 = &i; +const void* pointer_251 = &i; +const void* pointer_252 = &i; +const void* pointer_253 = &i; +const void* pointer_254 = &i; +const void* pointer_255 = &i; +const void* pointer_256 = &i; +const void* pointer_257 = &i; +const void* pointer_258 = &i; +const void* pointer_259 = &i; +const void* pointer_260 = &i; +const void* pointer_261 = &i; +const void* pointer_262 = &i; +const void* pointer_263 = &i; +const void* pointer_264 = &i; +const void* pointer_265 = &i; +const void* pointer_266 = &i; +const void* pointer_267 = &i; +const void* pointer_268 = &i; +const void* pointer_269 = &i; +const void* pointer_270 = &i; +const void* pointer_271 = &i; +const void* pointer_272 = &i; +const void* pointer_273 = &i; +const void* pointer_274 = &i; +const void* pointer_275 = &i; +const void* pointer_276 = &i; +const void* pointer_277 = &i; +const void* pointer_278 = &i; +const void* pointer_279 = &i; +const void* pointer_280 = &i; +const void* pointer_281 = &i; +const void* pointer_282 = &i; +const void* pointer_283 = &i; +const void* pointer_284 = &i; +const void* pointer_285 = &i; +const void* pointer_286 = &i; +const void* pointer_287 = &i; +const void* pointer_288 = &i; +const void* pointer_289 = &i; +const void* pointer_290 = &i; +const void* pointer_291 = &i; +const void* pointer_292 = &i; +const void* pointer_293 = &i; +const void* pointer_294 = &i; +const void* pointer_295 = &i; +const void* pointer_296 = &i; +const void* pointer_297 = &i; +const void* pointer_298 = &i; +const void* pointer_299 = &i; +const void* pointer_300 = &i; +const void* pointer_301 = &i; +const void* pointer_302 = &i; +const void* pointer_303 = &i; +const void* pointer_304 = &i; +const void* pointer_305 = &i; +const void* pointer_306 = &i; +const void* pointer_307 = &i; +const void* pointer_308 = &i; +const void* pointer_309 = &i; +const void* pointer_310 = &i; +const void* pointer_311 = &i; +const void* pointer_312 = &i; +const void* pointer_313 = &i; +const void* pointer_314 = &i; +const void* pointer_315 = &i; +const void* pointer_316 = &i; +const void* pointer_317 = &i; +const void* pointer_318 = &i; +const void* pointer_319 = &i; +const void* pointer_320 = &i; +const void* pointer_321 = &i; +const void* pointer_322 = &i; +const void* pointer_323 = &i; +const void* pointer_324 = &i; +const void* pointer_325 = &i; +const void* pointer_326 = &i; +const void* pointer_327 = &i; +const void* pointer_328 = &i; +const void* pointer_329 = &i; +const void* pointer_330 = &i; +const void* pointer_331 = &i; +const void* pointer_332 = &i; +const void* pointer_333 = &i; +const void* pointer_334 = &i; +const void* pointer_335 = &i; +const void* pointer_336 = &i; +const void* pointer_337 = &i; +const void* pointer_338 = &i; +const void* pointer_339 = &i; +const void* pointer_340 = &i; +const void* pointer_341 = &i; +const void* pointer_342 = &i; +const void* pointer_343 = &i; +const void* pointer_344 = &i; +const void* pointer_345 = &i; +const void* pointer_346 = &i; +const void* pointer_347 = &i; +const void* pointer_348 = &i; +const void* pointer_349 = &i; +const void* pointer_350 = &i; +const void* pointer_351 = &i; +const void* pointer_352 = &i; +const void* pointer_353 = &i; +const void* pointer_354 = &i; +const void* pointer_355 = &i; +const void* pointer_356 = &i; +const void* pointer_357 = &i; +const void* pointer_358 = &i; +const void* pointer_359 = &i; +const void* pointer_360 = &i; +const void* pointer_361 = &i; +const void* pointer_362 = &i; +const void* pointer_363 = &i; +const void* pointer_364 = &i; +const void* pointer_365 = &i; +const void* pointer_366 = &i; +const void* pointer_367 = &i; +const void* pointer_368 = &i; +const void* pointer_369 = &i; +const void* pointer_370 = &i; +const void* pointer_371 = &i; +const void* pointer_372 = &i; +const void* pointer_373 = &i; +const void* pointer_374 = &i; +const void* pointer_375 = &i; +const void* pointer_376 = &i; +const void* pointer_377 = &i; +const void* pointer_378 = &i; +const void* pointer_379 = &i; +const void* pointer_380 = &i; +const void* pointer_381 = &i; +const void* pointer_382 = &i; +const void* pointer_383 = &i; +const void* pointer_384 = &i; +const void* pointer_385 = &i; +const void* pointer_386 = &i; +const void* pointer_387 = &i; +const void* pointer_388 = &i; +const void* pointer_389 = &i; +const void* pointer_390 = &i; +const void* pointer_391 = &i; +const void* pointer_392 = &i; +const void* pointer_393 = &i; +const void* pointer_394 = &i; +const void* pointer_395 = &i; +const void* pointer_396 = &i; +const void* pointer_397 = &i; +const void* pointer_398 = &i; +const void* pointer_399 = &i; +const void* pointer_400 = &i; +const void* pointer_401 = &i; +const void* pointer_402 = &i; +const void* pointer_403 = &i; +const void* pointer_404 = &i; +const void* pointer_405 = &i; +const void* pointer_406 = &i; +const void* pointer_407 = &i; +const void* pointer_408 = &i; +const void* pointer_409 = &i; +const void* pointer_410 = &i; +const void* pointer_411 = &i; +const void* pointer_412 = &i; +const void* pointer_413 = &i; +const void* pointer_414 = &i; +const void* pointer_415 = &i; +const void* pointer_416 = &i; +const void* pointer_417 = &i; +const void* pointer_418 = &i; +const void* pointer_419 = &i; +const void* pointer_420 = &i; +const void* pointer_421 = &i; +const void* pointer_422 = &i; +const void* pointer_423 = &i; +const void* pointer_424 = &i; +const void* pointer_425 = &i; +const void* pointer_426 = &i; +const void* pointer_427 = &i; +const void* pointer_428 = &i; +const void* pointer_429 = &i; +const void* pointer_430 = &i; +const void* pointer_431 = &i; +const void* pointer_432 = &i; +const void* pointer_433 = &i; +const void* pointer_434 = &i; +const void* pointer_435 = &i; +const void* pointer_436 = &i; +const void* pointer_437 = &i; +const void* pointer_438 = &i; +const void* pointer_439 = &i; +const void* pointer_440 = &i; +const void* pointer_441 = &i; +const void* pointer_442 = &i; +const void* pointer_443 = &i; +const void* pointer_444 = &i; +const void* pointer_445 = &i; +const void* pointer_446 = &i; +const void* pointer_447 = &i; +const void* pointer_448 = &i; +const void* pointer_449 = &i; +const void* pointer_450 = &i; +const void* pointer_451 = &i; +const void* pointer_452 = &i; +const void* pointer_453 = &i; +const void* pointer_454 = &i; +const void* pointer_455 = &i; +const void* pointer_456 = &i; +const void* pointer_457 = &i; +const void* pointer_458 = &i; +const void* pointer_459 = &i; +const void* pointer_460 = &i; +const void* pointer_461 = &i; +const void* pointer_462 = &i; +const void* pointer_463 = &i; +const void* pointer_464 = &i; +const void* pointer_465 = &i; +const void* pointer_466 = &i; +const void* pointer_467 = &i; +const void* pointer_468 = &i; +const void* pointer_469 = &i; +const void* pointer_470 = &i; +const void* pointer_471 = &i; +const void* pointer_472 = &i; +const void* pointer_473 = &i; +const void* pointer_474 = &i; +const void* pointer_475 = &i; +const void* pointer_476 = &i; +const void* pointer_477 = &i; +const void* pointer_478 = &i; +const void* pointer_479 = &i; +const void* pointer_480 = &i; +const void* pointer_481 = &i; +const void* pointer_482 = &i; +const void* pointer_483 = &i; +const void* pointer_484 = &i; +const void* pointer_485 = &i; +const void* pointer_486 = &i; +const void* pointer_487 = &i; +const void* pointer_488 = &i; +const void* pointer_489 = &i; +const void* pointer_490 = &i; +const void* pointer_491 = &i; +const void* pointer_492 = &i; +const void* pointer_493 = &i; +const void* pointer_494 = &i; +const void* pointer_495 = &i; +const void* pointer_496 = &i; +const void* pointer_497 = &i; +const void* pointer_498 = &i; +const void* pointer_499 = &i; +const void* pointer_500 = &i; +const void* pointer_501 = &i; +const void* pointer_502 = &i; +const void* pointer_503 = &i; +const void* pointer_504 = &i; +const void* pointer_505 = &i; +const void* pointer_506 = &i; +const void* pointer_507 = &i; +const void* pointer_508 = &i; +const void* pointer_509 = &i; +const void* pointer_510 = &i; +const void* pointer_511 = &i; +const void* pointer_512 = &i; +const void* pointer_513 = &i; +const void* pointer_514 = &i; +const void* pointer_515 = &i; +const void* pointer_516 = &i; +const void* pointer_517 = &i; +const void* pointer_518 = &i; +const void* pointer_519 = &i; +const void* pointer_520 = &i; +const void* pointer_521 = &i; +const void* pointer_522 = &i; +const void* pointer_523 = &i; +const void* pointer_524 = &i; +const void* pointer_525 = &i; +const void* pointer_526 = &i; +const void* pointer_527 = &i; +const void* pointer_528 = &i; +const void* pointer_529 = &i; +const void* pointer_530 = &i; +const void* pointer_531 = &i; +const void* pointer_532 = &i; +const void* pointer_533 = &i; +const void* pointer_534 = &i; +const void* pointer_535 = &i; +const void* pointer_536 = &i; +const void* pointer_537 = &i; +const void* pointer_538 = &i; +const void* pointer_539 = &i; +const void* pointer_540 = &i; +const void* pointer_541 = &i; +const void* pointer_542 = &i; +const void* pointer_543 = &i; +const void* pointer_544 = &i; +const void* pointer_545 = &i; +const void* pointer_546 = &i; +const void* pointer_547 = &i; +const void* pointer_548 = &i; +const void* pointer_549 = &i; +const void* pointer_550 = &i; +const void* pointer_551 = &i; +const void* pointer_552 = &i; +const void* pointer_553 = &i; +const void* pointer_554 = &i; +const void* pointer_555 = &i; +const void* pointer_556 = &i; +const void* pointer_557 = &i; +const void* pointer_558 = &i; +const void* pointer_559 = &i; +const void* pointer_560 = &i; +const void* pointer_561 = &i; +const void* pointer_562 = &i; +const void* pointer_563 = &i; +const void* pointer_564 = &i; +const void* pointer_565 = &i; +const void* pointer_566 = &i; +const void* pointer_567 = &i; +const void* pointer_568 = &i; +const void* pointer_569 = &i; +const void* pointer_570 = &i; +const void* pointer_571 = &i; +const void* pointer_572 = &i; +const void* pointer_573 = &i; +const void* pointer_574 = &i; +const void* pointer_575 = &i; +const void* pointer_576 = &i; +const void* pointer_577 = &i; +const void* pointer_578 = &i; +const void* pointer_579 = &i; +const void* pointer_580 = &i; +const void* pointer_581 = &i; +const void* pointer_582 = &i; +const void* pointer_583 = &i; +const void* pointer_584 = &i; +const void* pointer_585 = &i; +const void* pointer_586 = &i; +const void* pointer_587 = &i; +const void* pointer_588 = &i; +const void* pointer_589 = &i; +const void* pointer_590 = &i; +const void* pointer_591 = &i; +const void* pointer_592 = &i; +const void* pointer_593 = &i; +const void* pointer_594 = &i; +const void* pointer_595 = &i; +const void* pointer_596 = &i; +const void* pointer_597 = &i; +const void* pointer_598 = &i; +const void* pointer_599 = &i; +const void* pointer_600 = &i; +const void* pointer_601 = &i; +const void* pointer_602 = &i; +const void* pointer_603 = &i; +const void* pointer_604 = &i; +const void* pointer_605 = &i; +const void* pointer_606 = &i; +const void* pointer_607 = &i; +const void* pointer_608 = &i; +const void* pointer_609 = &i; +const void* pointer_610 = &i; +const void* pointer_611 = &i; +const void* pointer_612 = &i; +const void* pointer_613 = &i; +const void* pointer_614 = &i; +const void* pointer_615 = &i; +const void* pointer_616 = &i; +const void* pointer_617 = &i; +const void* pointer_618 = &i; +const void* pointer_619 = &i; +const void* pointer_620 = &i; +const void* pointer_621 = &i; +const void* pointer_622 = &i; +const void* pointer_623 = &i; +const void* pointer_624 = &i; +const void* pointer_625 = &i; +const void* pointer_626 = &i; +const void* pointer_627 = &i; +const void* pointer_628 = &i; +const void* pointer_629 = &i; +const void* pointer_630 = &i; +const void* pointer_631 = &i; +const void* pointer_632 = &i; +const void* pointer_633 = &i; +const void* pointer_634 = &i; +const void* pointer_635 = &i; +const void* pointer_636 = &i; +const void* pointer_637 = &i; +const void* pointer_638 = &i; +const void* pointer_639 = &i; +const void* pointer_640 = &i; +const void* pointer_641 = &i; +const void* pointer_642 = &i; +const void* pointer_643 = &i; +const void* pointer_644 = &i; +const void* pointer_645 = &i; +const void* pointer_646 = &i; +const void* pointer_647 = &i; +const void* pointer_648 = &i; +const void* pointer_649 = &i; +const void* pointer_650 = &i; +const void* pointer_651 = &i; +const void* pointer_652 = &i; +const void* pointer_653 = &i; +const void* pointer_654 = &i; +const void* pointer_655 = &i; +const void* pointer_656 = &i; +const void* pointer_657 = &i; +const void* pointer_658 = &i; +const void* pointer_659 = &i; +const void* pointer_660 = &i; +const void* pointer_661 = &i; +const void* pointer_662 = &i; +const void* pointer_663 = &i; +const void* pointer_664 = &i; +const void* pointer_665 = &i; +const void* pointer_666 = &i; +const void* pointer_667 = &i; +const void* pointer_668 = &i; +const void* pointer_669 = &i; +const void* pointer_670 = &i; +const void* pointer_671 = &i; +const void* pointer_672 = &i; +const void* pointer_673 = &i; +const void* pointer_674 = &i; +const void* pointer_675 = &i; +const void* pointer_676 = &i; +const void* pointer_677 = &i; +const void* pointer_678 = &i; +const void* pointer_679 = &i; +const void* pointer_680 = &i; +const void* pointer_681 = &i; +const void* pointer_682 = &i; +const void* pointer_683 = &i; +const void* pointer_684 = &i; +const void* pointer_685 = &i; +const void* pointer_686 = &i; +const void* pointer_687 = &i; +const void* pointer_688 = &i; +const void* pointer_689 = &i; +const void* pointer_690 = &i; +const void* pointer_691 = &i; +const void* pointer_692 = &i; +const void* pointer_693 = &i; +const void* pointer_694 = &i; +const void* pointer_695 = &i; +const void* pointer_696 = &i; +const void* pointer_697 = &i; +const void* pointer_698 = &i; +const void* pointer_699 = &i; +const void* pointer_700 = &i; +const void* pointer_701 = &i; +const void* pointer_702 = &i; +const void* pointer_703 = &i; +const void* pointer_704 = &i; +const void* pointer_705 = &i; +const void* pointer_706 = &i; +const void* pointer_707 = &i; +const void* pointer_708 = &i; +const void* pointer_709 = &i; +const void* pointer_710 = &i; +const void* pointer_711 = &i; +const void* pointer_712 = &i; +const void* pointer_713 = &i; +const void* pointer_714 = &i; +const void* pointer_715 = &i; +const void* pointer_716 = &i; +const void* pointer_717 = &i; +const void* pointer_718 = &i; +const void* pointer_719 = &i; +const void* pointer_720 = &i; +const void* pointer_721 = &i; +const void* pointer_722 = &i; +const void* pointer_723 = &i; +const void* pointer_724 = &i; +const void* pointer_725 = &i; +const void* pointer_726 = &i; +const void* pointer_727 = &i; +const void* pointer_728 = &i; +const void* pointer_729 = &i; +const void* pointer_730 = &i; +const void* pointer_731 = &i; +const void* pointer_732 = &i; +const void* pointer_733 = &i; +const void* pointer_734 = &i; +const void* pointer_735 = &i; +const void* pointer_736 = &i; +const void* pointer_737 = &i; +const void* pointer_738 = &i; +const void* pointer_739 = &i; +const void* pointer_740 = &i; +const void* pointer_741 = &i; +const void* pointer_742 = &i; +const void* pointer_743 = &i; +const void* pointer_744 = &i; +const void* pointer_745 = &i; +const void* pointer_746 = &i; +const void* pointer_747 = &i; +const void* pointer_748 = &i; +const void* pointer_749 = &i; +const void* pointer_750 = &i; +const void* pointer_751 = &i; +const void* pointer_752 = &i; +const void* pointer_753 = &i; +const void* pointer_754 = &i; +const void* pointer_755 = &i; +const void* pointer_756 = &i; +const void* pointer_757 = &i; +const void* pointer_758 = &i; +const void* pointer_759 = &i; +const void* pointer_760 = &i; +const void* pointer_761 = &i; +const void* pointer_762 = &i; +const void* pointer_763 = &i; +const void* pointer_764 = &i; +const void* pointer_765 = &i; +const void* pointer_766 = &i; +const void* pointer_767 = &i; +const void* pointer_768 = &i; +const void* pointer_769 = &i; +const void* pointer_770 = &i; +const void* pointer_771 = &i; +const void* pointer_772 = &i; +const void* pointer_773 = &i; +const void* pointer_774 = &i; +const void* pointer_775 = &i; +const void* pointer_776 = &i; +const void* pointer_777 = &i; +const void* pointer_778 = &i; +const void* pointer_779 = &i; +const void* pointer_780 = &i; +const void* pointer_781 = &i; +const void* pointer_782 = &i; +const void* pointer_783 = &i; +const void* pointer_784 = &i; +const void* pointer_785 = &i; +const void* pointer_786 = &i; +const void* pointer_787 = &i; +const void* pointer_788 = &i; +const void* pointer_789 = &i; +const void* pointer_790 = &i; +const void* pointer_791 = &i; +const void* pointer_792 = &i; +const void* pointer_793 = &i; +const void* pointer_794 = &i; +const void* pointer_795 = &i; +const void* pointer_796 = &i; +const void* pointer_797 = &i; +const void* pointer_798 = &i; +const void* pointer_799 = &i; +const void* pointer_800 = &i; +const void* pointer_801 = &i; +const void* pointer_802 = &i; +const void* pointer_803 = &i; +const void* pointer_804 = &i; +const void* pointer_805 = &i; +const void* pointer_806 = &i; +const void* pointer_807 = &i; +const void* pointer_808 = &i; +const void* pointer_809 = &i; +const void* pointer_810 = &i; +const void* pointer_811 = &i; +const void* pointer_812 = &i; +const void* pointer_813 = &i; +const void* pointer_814 = &i; +const void* pointer_815 = &i; +const void* pointer_816 = &i; +const void* pointer_817 = &i; +const void* pointer_818 = &i; +const void* pointer_819 = &i; +const void* pointer_820 = &i; +const void* pointer_821 = &i; +const void* pointer_822 = &i; +const void* pointer_823 = &i; +const void* pointer_824 = &i; +const void* pointer_825 = &i; +const void* pointer_826 = &i; +const void* pointer_827 = &i; +const void* pointer_828 = &i; +const void* pointer_829 = &i; +const void* pointer_830 = &i; +const void* pointer_831 = &i; +const void* pointer_832 = &i; +const void* pointer_833 = &i; +const void* pointer_834 = &i; +const void* pointer_835 = &i; +const void* pointer_836 = &i; +const void* pointer_837 = &i; +const void* pointer_838 = &i; +const void* pointer_839 = &i; +const void* pointer_840 = &i; +const void* pointer_841 = &i; +const void* pointer_842 = &i; +const void* pointer_843 = &i; +const void* pointer_844 = &i; +const void* pointer_845 = &i; +const void* pointer_846 = &i; +const void* pointer_847 = &i; +const void* pointer_848 = &i; +const void* pointer_849 = &i; +const void* pointer_850 = &i; +const void* pointer_851 = &i; +const void* pointer_852 = &i; +const void* pointer_853 = &i; +const void* pointer_854 = &i; +const void* pointer_855 = &i; +const void* pointer_856 = &i; +const void* pointer_857 = &i; +const void* pointer_858 = &i; +const void* pointer_859 = &i; +const void* pointer_860 = &i; +const void* pointer_861 = &i; +const void* pointer_862 = &i; +const void* pointer_863 = &i; +const void* pointer_864 = &i; +const void* pointer_865 = &i; +const void* pointer_866 = &i; +const void* pointer_867 = &i; +const void* pointer_868 = &i; +const void* pointer_869 = &i; +const void* pointer_870 = &i; +const void* pointer_871 = &i; +const void* pointer_872 = &i; +const void* pointer_873 = &i; +const void* pointer_874 = &i; +const void* pointer_875 = &i; +const void* pointer_876 = &i; +const void* pointer_877 = &i; +const void* pointer_878 = &i; +const void* pointer_879 = &i; +const void* pointer_880 = &i; +const void* pointer_881 = &i; +const void* pointer_882 = &i; +const void* pointer_883 = &i; +const void* pointer_884 = &i; +const void* pointer_885 = &i; +const void* pointer_886 = &i; +const void* pointer_887 = &i; +const void* pointer_888 = &i; +const void* pointer_889 = &i; +const void* pointer_890 = &i; +const void* pointer_891 = &i; +const void* pointer_892 = &i; +const void* pointer_893 = &i; +const void* pointer_894 = &i; +const void* pointer_895 = &i; +const void* pointer_896 = &i; +const void* pointer_897 = &i; +const void* pointer_898 = &i; +const void* pointer_899 = &i; +const void* pointer_900 = &i; +const void* pointer_901 = &i; +const void* pointer_902 = &i; +const void* pointer_903 = &i; +const void* pointer_904 = &i; +const void* pointer_905 = &i; +const void* pointer_906 = &i; +const void* pointer_907 = &i; +const void* pointer_908 = &i; +const void* pointer_909 = &i; +const void* pointer_910 = &i; +const void* pointer_911 = &i; +const void* pointer_912 = &i; +const void* pointer_913 = &i; +const void* pointer_914 = &i; +const void* pointer_915 = &i; +const void* pointer_916 = &i; +const void* pointer_917 = &i; +const void* pointer_918 = &i; +const void* pointer_919 = &i; +const void* pointer_920 = &i; +const void* pointer_921 = &i; +const void* pointer_922 = &i; +const void* pointer_923 = &i; +const void* pointer_924 = &i; +const void* pointer_925 = &i; +const void* pointer_926 = &i; +const void* pointer_927 = &i; +const void* pointer_928 = &i; +const void* pointer_929 = &i; +const void* pointer_930 = &i; +const void* pointer_931 = &i; +const void* pointer_932 = &i; +const void* pointer_933 = &i; +const void* pointer_934 = &i; +const void* pointer_935 = &i; +const void* pointer_936 = &i; +const void* pointer_937 = &i; +const void* pointer_938 = &i; +const void* pointer_939 = &i; +const void* pointer_940 = &i; +const void* pointer_941 = &i; +const void* pointer_942 = &i; +const void* pointer_943 = &i; +const void* pointer_944 = &i; +const void* pointer_945 = &i; +const void* pointer_946 = &i; +const void* pointer_947 = &i; +const void* pointer_948 = &i; +const void* pointer_949 = &i; +const void* pointer_950 = &i; +const void* pointer_951 = &i; +const void* pointer_952 = &i; +const void* pointer_953 = &i; +const void* pointer_954 = &i; +const void* pointer_955 = &i; +const void* pointer_956 = &i; +const void* pointer_957 = &i; +const void* pointer_958 = &i; +const void* pointer_959 = &i; +const void* pointer_960 = &i; +const void* pointer_961 = &i; +const void* pointer_962 = &i; +const void* pointer_963 = &i; +const void* pointer_964 = &i; +const void* pointer_965 = &i; +const void* pointer_966 = &i; +const void* pointer_967 = &i; +const void* pointer_968 = &i; +const void* pointer_969 = &i; +const void* pointer_970 = &i; +const void* pointer_971 = &i; +const void* pointer_972 = &i; +const void* pointer_973 = &i; +const void* pointer_974 = &i; +const void* pointer_975 = &i; +const void* pointer_976 = &i; +const void* pointer_977 = &i; +const void* pointer_978 = &i; +const void* pointer_979 = &i; +const void* pointer_980 = &i; +const void* pointer_981 = &i; +const void* pointer_982 = &i; +const void* pointer_983 = &i; +const void* pointer_984 = &i; +const void* pointer_985 = &i; +const void* pointer_986 = &i; +const void* pointer_987 = &i; +const void* pointer_988 = &i; +const void* pointer_989 = &i; +const void* pointer_990 = &i; +const void* pointer_991 = &i; +const void* pointer_992 = &i; +const void* pointer_993 = &i; +const void* pointer_994 = &i; +const void* pointer_995 = &i; +const void* pointer_996 = &i; +const void* pointer_997 = &i; +const void* pointer_998 = &i; +const void* pointer_999 = &i; diff --git a/engine/src/flutter/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py b/engine/src/flutter/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py new file mode 100755 index 0000000000..e71b5cbbdc --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# +# Copyright 2014 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. + +"""Build relocation packer unit test data. + +Uses a built relocation packer to generate 'golden' reference test data +files for elf_file_unittests.cc. +""" + +import optparse +import os +import shutil +import subprocess +import sys +import tempfile + +def PackArmLibraryRelocations(android_pack_relocations, + android_objcopy, + added_section, + input_path, + output_path): + # Copy and add a 'NULL' .android.rel.dyn section for the packing tool. + with tempfile.NamedTemporaryFile() as stream: + stream.write('NULL') + stream.flush() + objcopy_command = [android_objcopy, + '--add-section', '%s=%s' % (added_section, stream.name), + input_path, output_path] + subprocess.check_call(objcopy_command) + + # Pack relocations. + pack_command = [android_pack_relocations, output_path] + subprocess.check_call(pack_command) + + +def UnpackArmLibraryRelocations(android_pack_relocations, + input_path, + output_path): + shutil.copy(input_path, output_path) + + # Unpack relocations. We leave the .android.rel.dyn or .android.rela.dyn + # in place. + unpack_command = [android_pack_relocations, '-u', output_path] + subprocess.check_call(unpack_command) + + +def main(): + parser = optparse.OptionParser() + + parser.add_option('--android-pack-relocations', + help='Path to the ARM relocations packer binary') + parser.add_option('--android-objcopy', + help='Path to the toolchain\'s objcopy binary') + parser.add_option('--added-section', + choices=['.android.rel.dyn', '.android.rela.dyn'], + help='Section to add, one of ".android.rel.dyn" or ".android.rela.dyn"') + parser.add_option('--test-file', + help='Path to the input test file, an unpacked ARM .so') + parser.add_option('--unpacked-output', + help='Path to the output file for reference unpacked data') + parser.add_option('--packed-output', + help='Path to the output file for reference packed data') + + options, _ = parser.parse_args() + + for output in [options.unpacked_output, options.packed_output]: + directory = os.path.dirname(output) + if not os.path.exists(directory): + os.makedirs(directory) + + PackArmLibraryRelocations(options.android_pack_relocations, + options.android_objcopy, + options.added_section, + options.test_file, + options.packed_output) + + UnpackArmLibraryRelocations(options.android_pack_relocations, + options.packed_output, + options.unpacked_output) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh b/engine/src/flutter/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh new file mode 100755 index 0000000000..f90a2f658f --- /dev/null +++ b/engine/src/flutter/tools/relocation_packer/test_data/generate_elf_file_unittest_relocs.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# Copyright 2014 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. + +# Generates elf_file_unittest_relocs_arm{32,64}{,_packed}.so test data files +# from elf_file_unittest_relocs.cc. Run once to create these test data +# files; the files are checked into the source tree. +# +# To use: +# ./generate_elf_file_unittest_relocs.sh +# git add elf_file_unittest_relocs_arm{32,64}{,_packed}.so + +function main() { + local '-r' test_data_directory="$(pwd)" + cd '../../..' + + source tools/cr/cr-bash-helpers.sh + local arch + for arch in 'arm32' 'arm64'; do + cr 'init' '--platform=android' '--type=Debug' '--architecture='"${arch}" + cr 'build' 'relocation_packer_unittests_test_data' + done + + local '-r' packer='out_android/Debug/obj/tools/relocation_packer' + local '-r' gen="${packer}/relocation_packer_unittests_test_data.gen" + + cp "${gen}/elf_file_unittest_relocs_arm"{32,64}{,_packed}'.so' \ + "${test_data_directory}" + + return 0 +} + +main diff --git a/engine/src/flutter/tools/remove_stale_pyc_files.py b/engine/src/flutter/tools/remove_stale_pyc_files.py new file mode 100755 index 0000000000..b32c5f4d26 --- /dev/null +++ b/engine/src/flutter/tools/remove_stale_pyc_files.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# Copyright 2014 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. + +import os +import sys + + +def RemoveAllStalePycFiles(base_dir): + """Scan directories for old .pyc files without a .py file and delete them.""" + for dirname, _, filenames in os.walk(base_dir): + if '.svn' in dirname or '.git' in dirname: + continue + for filename in filenames: + root, ext = os.path.splitext(filename) + if ext != '.pyc': + continue + + pyc_path = os.path.join(dirname, filename) + py_path = os.path.join(dirname, root + '.py') + + try: + if not os.path.exists(py_path): + os.remove(pyc_path) + except OSError: + # Wrap OS calls in try/except in case another process touched this file. + pass + + try: + os.removedirs(dirname) + except OSError: + # Wrap OS calls in try/except in case another process touched this dir. + pass + + +if __name__ == '__main__': + for path in sys.argv[1:]: + RemoveAllStalePycFiles(path) diff --git a/engine/src/flutter/tools/resources/about_credits.tmpl b/engine/src/flutter/tools/resources/about_credits.tmpl new file mode 100644 index 0000000000..04ec6fdb63 --- /dev/null +++ b/engine/src/flutter/tools/resources/about_credits.tmpl @@ -0,0 +1,76 @@ +#!mojo mojo:sky_viewer + + + +Credits + + + + + +Credits +
+{{entries}} +
+
+ + diff --git a/engine/src/flutter/tools/resources/about_credits_entry.tmpl b/engine/src/flutter/tools/resources/about_credits_entry.tmpl new file mode 100644 index 0000000000..99b354cf89 --- /dev/null +++ b/engine/src/flutter/tools/resources/about_credits_entry.tmpl @@ -0,0 +1,8 @@ +
+{{name}} +homepage +
+
{{license}}
+
+
+ diff --git a/engine/src/flutter/tools/sort-headers.py b/engine/src/flutter/tools/sort-headers.py new file mode 100755 index 0000000000..08031b2cfd --- /dev/null +++ b/engine/src/flutter/tools/sort-headers.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python +# Copyright (c) 2012 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. + +"""Given a filename as an argument, sort the #include/#imports in that file. + +Shows a diff and prompts for confirmation before doing the deed. +Works great with tools/git/for-all-touched-files.py. +""" + +import optparse +import os +import sys + + +def YesNo(prompt): + """Prompts with a yes/no question, returns True if yes.""" + print prompt, + sys.stdout.flush() + # http://code.activestate.com/recipes/134892/ + if sys.platform == 'win32': + import msvcrt + ch = msvcrt.getch() + else: + import termios + import tty + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + ch = 'n' + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + print ch + return ch in ('Y', 'y') + + +def IncludeCompareKey(line): + """Sorting comparator key used for comparing two #include lines. + Returns the filename without the #include/#import/import prefix. + """ + for prefix in ('#include ', '#import ', 'import '): + if line.startswith(prefix): + line = line[len(prefix):] + break + + # In sky, config.h must always be first to defined HAVE, USE, etc. + if line.startswith('"sky/engine/config.h"'): + return '0' + + # The win32 api has all sorts of implicit include order dependencies :-/ + # Give a few headers special sort keys that make sure they appear before all + # other headers. + if line.startswith(''): # Must be before e.g. shellapi.h + return '0' + if line.startswith(''): # Must be before atlapp.h. + return '1' + line + if line.startswith(''): # Must be before e.g. intshcut.h + return '1' + line + if line.startswith(''): # Must be before e.g. intshcut.h + return '1' + line + + # C++ system headers should come after C system headers. + if line.startswith('<'): + if line.find('.h>') != -1: + return '2' + line.lower() + else: + return '3' + line.lower() + + return '4' + line + + +def IsInclude(line): + """Returns True if the line is an #include/#import/import line.""" + return any([line.startswith('#include '), line.startswith('#import '), + line.startswith('import ')]) + + +def SortHeader(infile, outfile): + """Sorts the headers in infile, writing the sorted file to outfile.""" + for line in infile: + if IsInclude(line): + headerblock = [] + while IsInclude(line): + infile_ended_on_include_line = False + headerblock.append(line) + # Ensure we don't die due to trying to read beyond the end of the file. + try: + line = infile.next() + except StopIteration: + infile_ended_on_include_line = True + break + for header in sorted(headerblock, key=IncludeCompareKey): + outfile.write(header) + if infile_ended_on_include_line: + # We already wrote the last line above; exit to ensure it isn't written + # again. + return + # Intentionally fall through, to write the line that caused + # the above while loop to exit. + outfile.write(line) + + +def FixFileWithConfirmFunction(filename, confirm_function, + perform_safety_checks): + """Creates a fixed version of the file, invokes |confirm_function| + to decide whether to use the new file, and cleans up. + + |confirm_function| takes two parameters, the original filename and + the fixed-up filename, and returns True to use the fixed-up file, + false to not use it. + + If |perform_safety_checks| is True, then the function checks whether it is + unsafe to reorder headers in this file and skips the reorder with a warning + message in that case. + """ + if perform_safety_checks and IsUnsafeToReorderHeaders(filename): + print ('Not reordering headers in %s as the script thinks that the ' + 'order of headers in this file is semantically significant.' + % (filename)) + return + fixfilename = filename + '.new' + infile = open(filename, 'rb') + outfile = open(fixfilename, 'wb') + SortHeader(infile, outfile) + infile.close() + outfile.close() # Important so the below diff gets the updated contents. + + try: + if confirm_function(filename, fixfilename): + if sys.platform == 'win32': + os.unlink(filename) + os.rename(fixfilename, filename) + finally: + try: + os.remove(fixfilename) + except OSError: + # If the file isn't there, we don't care. + pass + + +def DiffAndConfirm(filename, should_confirm, perform_safety_checks): + """Shows a diff of what the tool would change the file named + filename to. Shows a confirmation prompt if should_confirm is true. + Saves the resulting file if should_confirm is false or the user + answers Y to the confirmation prompt. + """ + def ConfirmFunction(filename, fixfilename): + diff = os.system('diff -u %s %s' % (filename, fixfilename)) + if sys.platform != 'win32': + diff >>= 8 + if diff == 0: # Check exit code. + print '%s: no change' % filename + return False + + return (not should_confirm or YesNo('Use new file (y/N)?')) + + FixFileWithConfirmFunction(filename, ConfirmFunction, perform_safety_checks) + +def IsUnsafeToReorderHeaders(filename): + # *_message_generator.cc is almost certainly a file that generates IPC + # definitions. Changes in include order in these files can result in them not + # building correctly. + if filename.find("message_generator.cc") != -1: + return True + return False + +def main(): + parser = optparse.OptionParser(usage='%prog filename1 filename2 ...') + parser.add_option('-f', '--force', action='store_false', default=True, + dest='should_confirm', + help='Turn off confirmation prompt.') + parser.add_option('--no_safety_checks', + action='store_false', default=True, + dest='perform_safety_checks', + help='Do not perform the safety checks via which this ' + 'script refuses to operate on files for which it thinks ' + 'the include ordering is semantically significant.') + opts, filenames = parser.parse_args() + + if len(filenames) < 1: + parser.print_help() + return 1 + + for filename in filenames: + DiffAndConfirm(filename, opts.should_confirm, opts.perform_safety_checks) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/sort_sources.py b/engine/src/flutter/tools/sort_sources.py new file mode 100755 index 0000000000..bcbdbf67d9 --- /dev/null +++ b/engine/src/flutter/tools/sort_sources.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python +# Copyright 2015 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. + +"""Given a GYP/GN filename, sort C-ish source files in that file. + +Shows a diff and prompts for confirmation before doing the deed. +Works great with tools/git/for-all-touched-files.py. + +Limitations: + +1) Comments used as section headers + +If a comment (1+ lines starting with #) appears in a source list without a +preceding blank line, the tool assumes that the comment is about the next +line. For example, given the following source list, + + sources = [ + "b.cc", + # Comment. + "a.cc", + "c.cc", + ] + +the tool will produce the following output: + + sources = [ + # Comment. + "a.cc", + "b.cc", + "c.cc", + ] + +This is not correct if the comment is for starting a new section like: + + sources = [ + "b.cc", + # These are for Linux. + "a.cc", + "c.cc", + ] + +The tool cannot disambiguate the two types of comments. The problem can be +worked around by inserting a blank line before the comment because the tool +interprets a blank line as the end of a source list. + +2) Sources commented out + +Sometimes sources are commented out with their positions kept in the +alphabetical order, but what if the list is not sorted correctly? For +example, given the following source list, + + sources = [ + "a.cc", + # "b.cc", + "d.cc", + "c.cc", + ] + +the tool will produce the following output: + + sources = [ + "a.cc", + "c.cc", + # "b.cc", + "d.cc", + ] + +This is because the tool assumes that the comment (# "b.cc",) is about the +next line ("d.cc",). This kind of errors should be fixed manually, or the +commented-out code should be deleted. + +3) " and ' are used both used in the same source list (GYP only problem) + +If both " and ' are used in the same source list, sources quoted with " will +appear first in the output. The problem is rare enough so the tool does not +attempt to normalize them. Hence this kind of errors should be fixed +manually. + +4) Spaces and tabs used in the same source list + +Similarly, if spaces and tabs are both used in the same source list, sources +indented with tabs will appear first in the output. This kind of errors +should be fixed manually. + +""" + +import difflib +import optparse +import re +import sys + +from yes_no import YesNo + +SUFFIXES = ['c', 'cc', 'cpp', 'h', 'mm', 'rc', 'rc.version', 'ico', 'def', + 'release'] +SOURCE_PATTERN = re.compile(r'^\s+[\'"].*\.(%s)[\'"],$' % + '|'.join([re.escape(x) for x in SUFFIXES])) +COMMENT_PATTERN = re.compile(r'^\s+#') + + +def SortSources(original_lines): + """Sort source file names in |original_lines|. + + Args: + original_lines: Lines of the original content as a list of strings. + + Returns: + Lines of the sorted content as a list of strings. + + The algorithm is fairly naive. The code tries to find a list of C-ish + source file names by a simple regex, then sort them. The code does not try + to understand the syntax of the build files. See the file comment above for + details. + """ + + output_lines = [] + comments = [] + sources = [] + for line in original_lines: + if re.search(COMMENT_PATTERN, line): + comments.append(line) + elif re.search(SOURCE_PATTERN, line): + # Associate the line with the preceding comments. + sources.append([line, comments]) + comments = [] + else: + # |sources| should be flushed first, to handle comments at the end of a + # source list correctly. + if sources: + for source_line, source_comments in sorted(sources): + output_lines.extend(source_comments) + output_lines.append(source_line) + sources = [] + if comments: + output_lines.extend(comments) + comments = [] + output_lines.append(line) + return output_lines + + +def ProcessFile(filename, should_confirm): + """Process the input file and rewrite if needed. + + Args: + filename: Path to the input file. + should_confirm: If true, diff and confirmation prompt are shown. + """ + + original_lines = [] + with open(filename, 'r') as input_file: + for line in input_file: + original_lines.append(line) + + new_lines = SortSources(original_lines) + if original_lines == new_lines: + print '%s: no change' % filename + return + + if should_confirm: + diff = difflib.unified_diff(original_lines, new_lines) + sys.stdout.writelines(diff) + if not YesNo('Use new file (y/N)'): + return + + with open(filename, 'w') as output_file: + output_file.writelines(new_lines) + + +def main(): + parser = optparse.OptionParser(usage='%prog filename1 filename2 ...') + parser.add_option('-f', '--force', action='store_false', default=True, + dest='should_confirm', + help='Turn off confirmation prompt.') + opts, filenames = parser.parse_args() + + if len(filenames) < 1: + parser.print_help() + return 1 + + for filename in filenames: + ProcessFile(filename, opts.should_confirm) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/engine/src/flutter/tools/vim/chromium.ycm_extra_conf.py b/engine/src/flutter/tools/vim/chromium.ycm_extra_conf.py new file mode 100644 index 0000000000..76fce67611 --- /dev/null +++ b/engine/src/flutter/tools/vim/chromium.ycm_extra_conf.py @@ -0,0 +1,233 @@ +# Copyright (c) 2012 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. + +# Autocompletion config for YouCompleteMe in Chromium. +# +# USAGE: +# +# 1. Install YCM [https://github.com/Valloric/YouCompleteMe] +# (Googlers should check out [go/ycm]) +# +# 2. Point to this config file in your .vimrc: +# let g:ycm_global_ycm_extra_conf = +# '/src/tools/vim/chromium.ycm_extra_conf.py' +# +# 3. Profit +# +# +# Usage notes: +# +# * You must use ninja & clang to build Chromium. +# +# * You must have run gyp_chromium and built Chromium recently. +# +# +# Hacking notes: +# +# * The purpose of this script is to construct an accurate enough command line +# for YCM to pass to clang so it can build and extract the symbols. +# +# * Right now, we only pull the -I and -D flags. That seems to be sufficient +# for everything I've used it for. +# +# * That whole ninja & clang thing? We could support other configs if someone +# were willing to write the correct commands and a parser. +# +# * This has only been tested on gPrecise. + + +import os +import os.path +import re +import subprocess +import sys + + +def SystemIncludeDirectoryFlags(): + """Determines compile flags to include the system include directories. + + Use as a workaround for https://github.com/Valloric/YouCompleteMe/issues/303 + + Returns: + (List of Strings) Compile flags to append. + """ + try: + with open(os.devnull, 'rb') as DEVNULL: + output = subprocess.check_output(['clang', '-v', '-E', '-x', 'c++', '-'], + stdin=DEVNULL, stderr=subprocess.STDOUT) + except: + return [] + includes_regex = r'#include <\.\.\.> search starts here:\s*' \ + r'(.*?)End of search list\.' + includes = re.search(includes_regex, output.decode(), re.DOTALL).group(1) + flags = [] + for path in includes.splitlines(): + path = path.strip() + if os.path.isdir(path): + flags.append('-isystem') + flags.append(path) + return flags + + +_system_include_flags = SystemIncludeDirectoryFlags() + +# Flags from YCM's default config. +flags = [ +'-DUSE_CLANG_COMPLETER', +'-std=c++11', +'-x', +'c++', +] + + +def PathExists(*args): + return os.path.exists(os.path.join(*args)) + + +def FindChromeSrcFromFilename(filename): + """Searches for the root of the Chromium checkout. + + Simply checks parent directories until it finds .gclient and src/. + + Args: + filename: (String) Path to source file being edited. + + Returns: + (String) Path of 'src/', or None if unable to find. + """ + curdir = os.path.normpath(os.path.dirname(filename)) + while not (os.path.basename(os.path.realpath(curdir)) == 'src' + and PathExists(curdir, 'DEPS') + and (PathExists(curdir, '..', '.gclient') + or PathExists(curdir, '.git'))): + nextdir = os.path.normpath(os.path.join(curdir, '..')) + if nextdir == curdir: + return None + curdir = nextdir + return curdir + + +def GetClangCommandFromNinjaForFilename(chrome_root, filename): + """Returns the command line to build |filename|. + + Asks ninja how it would build the source file. If the specified file is a + header, tries to find its companion source file first. + + Args: + chrome_root: (String) Path to src/. + filename: (String) Path to source file being edited. + + Returns: + (List of Strings) Command line arguments for clang. + """ + if not chrome_root: + return [] + + # Generally, everyone benefits from including Chromium's src/, because all of + # Chromium's includes are relative to that. + chrome_flags = ['-I' + os.path.join(chrome_root)] + + # Version of Clang used to compile Chromium can be newer then version of + # libclang that YCM uses for completion. So it's possible that YCM's libclang + # doesn't know about some used warning options, which causes compilation + # warnings (and errors, because of '-Werror'); + chrome_flags.append('-Wno-unknown-warning-option') + + # Default file to get a reasonable approximation of the flags for a Blink + # file. + blink_root = os.path.join(chrome_root, 'third_party', 'WebKit') + default_blink_file = os.path.join(blink_root, 'Source', 'core', 'Init.cpp') + + # Header files can't be built. Instead, try to match a header file to its + # corresponding source file. + if filename.endswith('.h'): + # Add config.h to Blink headers, which won't have it by default. + if filename.startswith(blink_root): + chrome_flags.append('-include') + chrome_flags.append(os.path.join(blink_root, 'Source', 'config.h')) + + alternates = ['.cc', '.cpp'] + for alt_extension in alternates: + alt_name = filename[:-2] + alt_extension + if os.path.exists(alt_name): + filename = alt_name + break + else: + if filename.startswith(blink_root): + # If this is a Blink file, we can at least try to get a reasonable + # approximation. + filename = default_blink_file + else: + # If this is a standalone .h file with no source, the best we can do is + # try to use the default flags. + return chrome_flags + + sys.path.append(os.path.join(chrome_root, 'tools', 'vim')) + from ninja_output import GetNinjaOutputDirectory + out_dir = os.path.realpath(GetNinjaOutputDirectory(chrome_root)) + + # Ninja needs the path to the source file relative to the output build + # directory. + rel_filename = os.path.relpath(os.path.realpath(filename), out_dir) + + # Ask ninja how it would build our source file. + p = subprocess.Popen(['ninja', '-v', '-C', out_dir, '-t', + 'commands', rel_filename + '^'], + stdout=subprocess.PIPE) + stdout, stderr = p.communicate() + if p.returncode: + return chrome_flags + + # Ninja might execute several commands to build something. We want the last + # clang command. + clang_line = None + for line in reversed(stdout.split('\n')): + if 'clang' in line: + clang_line = line + break + else: + return chrome_flags + + # Parse flags that are important for YCM's purposes. + for flag in clang_line.split(' '): + if flag.startswith('-I'): + # Relative paths need to be resolved, because they're relative to the + # output dir, not the source. + if flag[2] == '/': + chrome_flags.append(flag) + else: + abs_path = os.path.normpath(os.path.join(out_dir, flag[2:])) + chrome_flags.append('-I' + abs_path) + elif flag.startswith('-std'): + chrome_flags.append(flag) + elif flag.startswith('-') and flag[1] in 'DWFfmO': + if flag == '-Wno-deprecated-register' or flag == '-Wno-header-guard': + # These flags causes libclang (3.3) to crash. Remove it until things + # are fixed. + continue + chrome_flags.append(flag) + + return chrome_flags + + +def FlagsForFile(filename): + """This is the main entry point for YCM. Its interface is fixed. + + Args: + filename: (String) Path to source file being edited. + + Returns: + (Dictionary) + 'flags': (List of Strings) Command line flags. + 'do_cache': (Boolean) True if the result should be cached. + """ + chrome_root = FindChromeSrcFromFilename(filename) + chrome_flags = GetClangCommandFromNinjaForFilename(chrome_root, + filename) + final_flags = flags + chrome_flags + _system_include_flags + + return { + 'flags': final_flags, + 'do_cache': True + } diff --git a/engine/src/flutter/tools/vim/clang-format.vim b/engine/src/flutter/tools/vim/clang-format.vim new file mode 100644 index 0000000000..982b8d2ed0 --- /dev/null +++ b/engine/src/flutter/tools/vim/clang-format.vim @@ -0,0 +1,19 @@ +" Copyright (c) 2014 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. + +" Binds cmd-shift-i (on Mac) or ctrl-shift-i (elsewhere) to invoking +" clang-format.py. +" It will format the current selection (and if there's no selection, the +" current line.) + +let s:script = expand(':p:h') . + \'/../../buildtools/clang_format/script/clang-format.py' + +if has('mac') + execute "map :pyf " . s:script . "" + execute "imap :pyf " . s:script . "i" +else + execute "map :pyf " . s:script . "" + execute "imap :pyf " . s:script . "i" +endif diff --git a/engine/src/flutter/tools/vim/filetypes.vim b/engine/src/flutter/tools/vim/filetypes.vim new file mode 100644 index 0000000000..3e7c8f9ead --- /dev/null +++ b/engine/src/flutter/tools/vim/filetypes.vim @@ -0,0 +1,9 @@ +" To get syntax highlighting and tab settings for gyp(i) and DEPS files, +" add the following to your .vimrc file: +" so /path/to/src/tools/vim/filetypes.vim + +augroup filetype + au! BufRead,BufNewFile *.gyp set filetype=python expandtab tabstop=2 shiftwidth=2 + au! BufRead,BufNewFile *.gypi set filetype=python expandtab tabstop=2 shiftwidth=2 + au! BufRead,BufNewFile DEPS set filetype=python expandtab tabstop=2 shiftwidth=2 +augroup END diff --git a/engine/src/flutter/tools/vim/ninja-build.vim b/engine/src/flutter/tools/vim/ninja-build.vim new file mode 100644 index 0000000000..6e14cba227 --- /dev/null +++ b/engine/src/flutter/tools/vim/ninja-build.vim @@ -0,0 +1,115 @@ +" Copyright (c) 2012 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. +" +" Adds a "Compile this file" function, using ninja. On Mac, binds Cmd-k to +" this command. On Windows, Ctrl-F7 (which is the same as the VS default). +" On Linux, o, which is \o by default ("o"=creates .o files) +" +" Adds a "Build this target" function, using ninja. This is not bound +" to any key by default, but can be used via the :CrBuild command. +" It builds 'chrome' by default, but :CrBuild target1 target2 etc works as well. +" +" Requires that gyp has already generated build.ninja files, and that ninja is +" in your path (which it is automatically if depot_tools is in your path). +" +" Add the following to your .vimrc file: +" so /path/to/src/tools/vim/ninja-build.vim + +python << endpython +import os +import vim + + +def path_to_current_buffer(): + """Returns the absolute path of the current buffer.""" + return vim.current.buffer.name + + +def path_to_source_root(): + """Returns the absolute path to the chromium source root.""" + candidate = os.path.dirname(path_to_current_buffer()) + # This is a list of files that need to identify the src directory. The shorter + # it is, the more likely it's wrong (checking for just "build/common.gypi" + # would find "src/v8" for files below "src/v8", as "src/v8/build/common.gypi" + # exists). The longer it is, the more likely it is to break when we rename + # directories. + fingerprints = ['chrome', 'net', 'v8', 'build', 'skia'] + while candidate and not all( + [os.path.isdir(os.path.join(candidate, fp)) for fp in fingerprints]): + candidate = os.path.dirname(candidate) + return candidate + + +def path_to_build_dir(configuration): + """Returns //(Release|Debug).""" + + chrome_root = path_to_source_root() + sys.path.append(os.path.join(chrome_root, 'tools', 'vim')) + from ninja_output import GetNinjaOutputDirectory + return GetNinjaOutputDirectory(chrome_root, configuration) + +def compute_ninja_command_for_current_buffer(configuration=None): + """Returns the shell command to compile the file in the current buffer.""" + build_dir = path_to_build_dir(configuration) + + # ninja needs filepaths for the ^ syntax to be relative to the + # build directory. + file_to_build = path_to_current_buffer() + file_to_build = os.path.relpath(file_to_build, build_dir) + + build_cmd = ' '.join(['ninja', '-C', build_dir, file_to_build + '^']) + if sys.platform == 'win32': + # Escape \ for Vim, and ^ for both Vim and shell. + build_cmd = build_cmd.replace('\\', '\\\\').replace('^', '^^^^') + vim.command('return "%s"' % build_cmd) + + +def compute_ninja_command_for_targets(targets='', configuration=None): + build_cmd = ' '.join(['ninja', '-C', path_to_build_dir(configuration), + targets]) + vim.command('return "%s"' % build_cmd) +endpython + +fun! s:MakeWithCustomCommand(build_cmd) + let l:oldmakepgr = &makeprg + let &makeprg=a:build_cmd + silent make | cwindow + if !has('gui_running') + redraw! + endif + let &makeprg = l:oldmakepgr +endfun + +fun! s:NinjaCommandForCurrentBuffer() + python compute_ninja_command_for_current_buffer() +endfun + +fun! s:NinjaCommandForTargets(targets) + python compute_ninja_command_for_targets(vim.eval('a:targets')) +endfun + +fun! CrCompileFile() + call s:MakeWithCustomCommand(s:NinjaCommandForCurrentBuffer()) +endfun + +fun! CrBuild(...) + let l:targets = a:0 > 0 ? join(a:000, ' ') : '' + if (l:targets !~ '\i') + let l:targets = 'chrome' + endif + call s:MakeWithCustomCommand(s:NinjaCommandForTargets(l:targets)) +endfun + +command! CrCompileFile call CrCompileFile() +command! -nargs=* CrBuild call CrBuild() + +if has('mac') + map :CrCompileFile + imap :CrCompileFile +elseif has('win32') + map :CrCompileFile + imap :CrCompileFile +elseif has('unix') + map o :CrCompileFile +endif diff --git a/engine/src/flutter/tools/vim/ninja_output.py b/engine/src/flutter/tools/vim/ninja_output.py new file mode 100644 index 0000000000..e343c5b7f2 --- /dev/null +++ b/engine/src/flutter/tools/vim/ninja_output.py @@ -0,0 +1,57 @@ +# Copyright 2014 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. + + +import os +import os.path +import re + + +def GetNinjaOutputDirectory(chrome_root, configuration=None): + """Returns //(Release|Debug). + + The output_dir is detected in the following ways, in order of precedence: + 1. CHROMIUM_OUT_DIR environment variable. + 2. GYP_GENERATOR_FLAGS environment variable output_dir property. + 3. Symlink target, if src/out is a symlink. + 4. Most recently modified (e.g. built) directory called out or out_*. + + The configuration chosen is the one most recently generated/built, but can be + overriden via the parameter.""" + + output_dirs = [] + if ('CHROMIUM_OUT_DIR' in os.environ and + os.path.isdir(os.path.join(chrome_root, os.environ['CHROMIUM_OUT_DIR']))): + output_dirs = [os.environ['CHROMIUM_OUT_DIR']] + if not output_dirs: + generator_flags = os.getenv('GYP_GENERATOR_FLAGS', '').split(' ') + for flag in generator_flags: + name_value = flag.split('=', 1) + if (len(name_value) == 2 and name_value[0] == 'output_dir' and + os.path.isdir(os.path.join(chrome_root, name_value[1]))): + output_dirs = [name_value[1]] + if not output_dirs: + out = os.path.join(chrome_root, 'out') + if os.path.islink(out): + out_target = os.path.join(os.path.dirname(out), os.readlink(out)) + if os.path.exists(out_target): + output_dirs = [out_target] + if not output_dirs: + for f in os.listdir(chrome_root): + if (re.match('out(?:$|_)', f) and + os.path.isdir(os.path.join(chrome_root, f))): + output_dirs.append(f) + + configs = [configuration] if configuration else ['Debug', 'Release'] + output_paths = [os.path.join(chrome_root, out_dir, config) + for out_dir in output_dirs for config in configs] + + def approx_directory_mtime(path): + if not os.path.exists(path): + return -1 + # This is a heuristic; don't recurse into subdirectories. + paths = [path] + [os.path.join(path, f) for f in os.listdir(path)] + return max(os.path.getmtime(p) for p in paths) + + return max(output_paths, key=approx_directory_mtime) diff --git a/engine/src/flutter/tools/xdisplaycheck/BUILD.gn b/engine/src/flutter/tools/xdisplaycheck/BUILD.gn new file mode 100644 index 0000000000..779e163651 --- /dev/null +++ b/engine/src/flutter/tools/xdisplaycheck/BUILD.gn @@ -0,0 +1,15 @@ +# Copyright 2014 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. + +executable("xdisplaycheck") { + sources = [ + "xdisplaycheck.cc", + ] + + configs += [ "//build/config/linux:x11" ] + + deps = [ + "//build/config/sanitizers:deps", + ] +} diff --git a/engine/src/flutter/tools/xdisplaycheck/xdisplaycheck.cc b/engine/src/flutter/tools/xdisplaycheck/xdisplaycheck.cc new file mode 100644 index 0000000000..6623153a84 --- /dev/null +++ b/engine/src/flutter/tools/xdisplaycheck/xdisplaycheck.cc @@ -0,0 +1,119 @@ +// Copyright (c) 2012 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. +// +// This is a small program that tries to connect to the X server. It +// continually retries until it connects or 30 seconds pass. If it fails +// to connect to the X server or fails to find needed functiona, it returns +// an error code of -1. +// +// This is to help verify that a useful X server is available before we start +// start running tests on the build bots. + +#include +#include +#include +#include +#include + +#if defined(USE_AURA) +#include +#endif + +void Sleep(int duration_ms) { + struct timespec sleep_time, remaining; + + // Contains the portion of duration_ms >= 1 sec. + sleep_time.tv_sec = duration_ms / 1000; + duration_ms -= sleep_time.tv_sec * 1000; + + // Contains the portion of duration_ms < 1 sec. + sleep_time.tv_nsec = duration_ms * 1000 * 1000; // nanoseconds. + + while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) + sleep_time = remaining; +} + +class XScopedDisplay { + public: + XScopedDisplay() : display_(NULL) {} + ~XScopedDisplay() { + if (display_) XCloseDisplay(display_); + } + + void set(Display* display) { display_ = display; } + Display* display() { return display_; } + + private: + Display* display_; +}; + +int main(int argc, char* argv[]) { + XScopedDisplay scoped_display; + if (argv[1] && strcmp(argv[1], "--noserver") == 0) { + scoped_display.set(XOpenDisplay(NULL)); + if (scoped_display.display()) { + fprintf(stderr, "Found unexpected connectable display %s\n", + XDisplayName(NULL)); + } + // Return success when we got an unexpected display so that the code + // without the --noserver is the same, but slow, rather than inverted. + return !scoped_display.display(); + } + + int kNumTries = 78; // 78*77/2 * 10 = 30s of waiting + int tries; + for (tries = 0; tries < kNumTries; ++tries) { + scoped_display.set(XOpenDisplay(NULL)); + if (scoped_display.display()) + break; + Sleep(10 * tries); + } + + if (!scoped_display.display()) { + fprintf(stderr, "Failed to connect to %s\n", XDisplayName(NULL)); + return -1; + } + + fprintf(stderr, "Connected after %d retries\n", tries); + +#if defined(USE_AURA) + // Check for XInput2 + int opcode, event, err; + if (!XQueryExtension(scoped_display.display(), "XInputExtension", &opcode, + &event, &err)) { + fprintf(stderr, + "Failed to get XInputExtension on %s.\n", XDisplayName(NULL)); + return -2; + } + + int major = 2, minor = 0; + if (XIQueryVersion(scoped_display.display(), &major, &minor) == BadRequest) { + fprintf(stderr, + "Server does not have XInput2 on %s.\n", XDisplayName(NULL)); + return -3; + } + + // Ask for the list of devices. This can cause some Xvfb to crash. + int count = 0; + XIDeviceInfo* devices = + XIQueryDevice(scoped_display.display(), XIAllDevices, &count); + if (devices) + XIFreeDeviceInfo(devices); + + fprintf(stderr, + "XInput2 verified initially sane on %s.\n", XDisplayName(NULL)); +#endif + return 0; +} + +#if defined(LEAK_SANITIZER) +// XOpenDisplay leaks memory if it takes more than one try to connect. This +// causes LSan bots to fail. We don't care about memory leaks in xdisplaycheck +// anyway, so just disable LSan completely. +// This function isn't referenced from the executable itself. Make sure it isn't +// stripped by the linker. +__attribute__((used)) +__attribute__((visibility("default"))) +extern "C" int __lsan_is_turned_off() { return 1; } +#endif diff --git a/engine/src/flutter/tools/xdisplaycheck/xdisplaycheck.gyp b/engine/src/flutter/tools/xdisplaycheck/xdisplaycheck.gyp new file mode 100644 index 0000000000..7030b6fdb7 --- /dev/null +++ b/engine/src/flutter/tools/xdisplaycheck/xdisplaycheck.gyp @@ -0,0 +1,19 @@ +# Copyright (c) 2009 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. + +{ + 'targets': [ + { + # GN version: //tools/xdisplaycheck + 'target_name': 'xdisplaycheck', + 'type': 'executable', + 'dependencies': [ + '../../build/linux/system.gyp:x11', + ], + 'sources': [ + 'xdisplaycheck.cc', + ], + }, + ], +} diff --git a/engine/src/flutter/tools/yes_no.py b/engine/src/flutter/tools/yes_no.py new file mode 100644 index 0000000000..8682ec8de5 --- /dev/null +++ b/engine/src/flutter/tools/yes_no.py @@ -0,0 +1,28 @@ +# Copyright 2015 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. + +import sys + + +def YesNo(prompt): + """Prompts with a yes/no question, returns True if yes.""" + print prompt, + sys.stdout.flush() + # http://code.activestate.com/recipes/134892/ + if sys.platform == 'win32': + import msvcrt + ch = msvcrt.getch() + else: + import termios + import tty + fd = sys.stdin.fileno() + old_settings = termios.tcgetattr(fd) + ch = 'n' + try: + tty.setraw(sys.stdin.fileno()) + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + print ch + return ch in ('Y', 'y')